xcb-types (empty) → 0.1.0
raw patch · 8 files changed
+741/−0 lines, 8 filesdep +basedep +mtldep +prettysetup-changed
Dependencies added: base, mtl, pretty, xml
Files
- Data/XCB.hs +23/−0
- Data/XCB/FromXML.hs +401/−0
- Data/XCB/Pretty.hs +131/−0
- Data/XCB/Types.hs +99/−0
- Data/XCB/Utils.hs +18/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- xcb-types.cabal +37/−0
+ Data/XCB.hs view
@@ -0,0 +1,23 @@+-- |+-- Module : Data.XCB+-- Copyright : (c) Antoine Latter 2008+-- License : BSD3+--+-- Maintainer: Antoine Latter <aslatter@gmail.com>+-- Stability : provisional+-- Portability: portable+--+-- The 'Data.XCB' module can parse the contents of the xcb-proto+-- XML files into Haskell data structures.+--+-- Pretty-printers are provided to aid in the debugging - they do+-- not pretty-print to XML, but to a custom human-readable format.+module Data.XCB+ (module Data.XCB.Types+ ,module Data.XCB.FromXML+ ,module Data.XCB.Pretty+ ) where++import Data.XCB.Types+import Data.XCB.FromXML+import Data.XCB.Pretty
+ Data/XCB/FromXML.hs view
@@ -0,0 +1,401 @@+-- |+-- Module : Data.XCB.FromXML+-- Copyright : (c) Antoine Latter 2008+-- License : BSD3+--+-- Maintainer: Antoine Latter <aslatter@gmail.com>+-- Stability : provisional+-- Portability: portable+--+-- Handls parsing the data structures from XML files.+--+-- In order to support copying events and errors across module+-- boundaries, all modules which may have cross-module event copies and+-- error copies must be parsed at once.+--+-- There is no provision for preserving the event copy and error copy+-- declarations - the copies are handled during parsing.+module Data.XCB.FromXML(fromFiles+ ,fromStrings+ ) where++import Data.XCB.Types+import Data.XCB.Pretty+import Data.XCB.Utils++import Text.XML.Light++import Data.List as List+import Data.Maybe+import Data.Monoid++import Control.Monad+import Control.Monad.Reader++import Control.Applicative++-- |Process the listed XML files.+-- Any files which fail to parse are silently dropped.+-- Any declaration in an XML file which fail to parse are+-- silently dropped.+fromFiles :: [FilePath] -> IO [XHeader]+fromFiles xs = do+ strings <- sequence $ map readFile xs+ return $ fromStrings strings++-- |Process the strings as if they were XML files.+-- Any files which fail to parse are silently dropped.+-- Any declaration in an XML file which fail to parse are+-- silently dropped.+fromStrings :: [String] -> [XHeader]+fromStrings xs =+ let rs = mapAlt fromString xs+ Just headers = runReaderT rs headers+ in headers ++-- The 'Parse' monad. Provides the name of the+-- current module, and a list of all of the modules.+type Parse = ReaderT ([XHeader],Name) Maybe++-- operations in the 'Parse' monad++localName :: Parse Name+localName = snd `liftM` ask++allModules :: Parse [XHeader]+allModules = fst `liftM` ask++-- a generic function for looking up something from+-- a named XHeader.+--+-- this implements searching both the current module and+-- the xproto module if the name is not specified.+lookupThingy :: ([XDecl] -> Maybe a)+ -> (Maybe Name)+ -> Parse (Maybe a)+lookupThingy f Nothing = do+ lname <- localName+ liftM2 mplus (lookupThingy f $ Just lname)+ (lookupThingy f $ Just "xproto") -- implicit xproto import+lookupThingy f (Just mname) = do+ xs <- allModules+ return $ do+ x <- findXHeader mname xs+ f $ xheader_decls x++-- lookup an event declaration by name.+lookupEvent :: Maybe Name -> Name -> Parse (Maybe EventDetails)+lookupEvent mname evname = flip lookupThingy mname $ \decls ->+ findEvent evname decls++-- lookup an error declaration by name.+lookupError :: Maybe Name -> Name -> Parse (Maybe ErrorDetails)+lookupError mname ername = flip lookupThingy mname $ \decls ->+ findError ername decls++findXHeader :: Name -> [XHeader] -> Maybe XHeader+findXHeader name = List.find $ \ x -> xheader_header x == name++findError :: Name -> [XDecl] -> Maybe ErrorDetails+findError pname xs =+ case List.find f xs of+ Nothing -> Nothing+ Just (XError name code elems) -> Just $ ErrorDetails name code elems+ where f (XError name _ _) | name == pname = True+ f _ = False + +findEvent :: Name -> [XDecl] -> Maybe EventDetails+findEvent pname xs = + case List.find f xs of+ Nothing -> Nothing+ Just (XEvent name code elems) -> Just $ EventDetails name code elems+ where f (XEvent name _ _) | name == pname = True+ f _ = False ++data EventDetails = EventDetails Name Int [StructElem]+data ErrorDetails = ErrorDetails Name Int [StructElem]++---++-- extract a single XHeader from a single XML document+fromString :: String -> ReaderT [XHeader] Maybe XHeader+fromString str = do+ el@(Element qname ats cnt _) <- lift $ parseXMLDoc str+ guard $ el `named` "xcb"+ header <- el `attr` "header"+ let name = el `attr` "extension-name"+ xname = el `attr` "extension-xname"+ maj_ver = el `attr` "major-version" >>= readM+ min_ver = el `attr` "minor-version" >>= readM+ multiword = el `attr` "extension-multiword" >>= readM . ensureUpper+ decls <- withReaderT (\r -> (r,header)) $ extractDecls cnt+ return $ XHeader {xheader_header = header+ ,xheader_xname = xname+ ,xheader_name = name+ ,xheader_multiword = multiword+ ,xheader_major_version = maj_ver+ ,xheader_minor_version = min_ver+ ,xheader_decls = decls+ }++-- attempts to extract declarations from XML content, discarding failures.+extractDecls :: [Content] -> Parse [XDecl]+extractDecls = mapAlt declFromElem . onlyElems++-- attempt to extract a module declaration from an XML element+declFromElem :: Element -> Parse XDecl+declFromElem elem + | elem `named` "request" = xrequest elem+ | elem `named` "event" = xevent elem+ | elem `named` "eventcopy" = xevcopy elem+ | elem `named` "error" = xerror elem+ | elem `named` "errorcopy" = xercopy elem+ | elem `named` "struct" = xstruct elem+ | elem `named` "union" = xunion elem+ | elem `named` "xidtype" = xidtype elem+ | elem `named` "xidunion" = xidunion elem+ | elem `named` "typedef" = xtypedef elem+ | elem `named` "enum" = xenum elem+ | elem `named` "import" = ximport elem+ | otherwise = mzero+++ximport :: Element -> Parse XDecl+ximport = return . XImport . strContent++xenum :: Element -> Parse XDecl+xenum elem = do+ nm <- elem `attr` "name"+ fields <- mapAlt enumField $ elChildren elem+ guard $ not $ null fields+ return $ XEnum nm fields++enumField :: Element -> Parse EnumElem+enumField elem = do+ guard $ elem `named` "item"+ name <- elem `attr` "name"+ let expr = firstChild elem >>= expression+ return $ EnumElem name expr++xrequest :: Element -> Parse XDecl+xrequest elem = do+ nm <- elem `attr` "name"+ code <- elem `attr` "opcode" >>= readM+ fields <- mapAlt structField $ elChildren elem+ let reply = getReply elem+ guard $ not (null fields) || not (isNothing reply)+ return $ XRequest nm code fields reply++getReply :: Element -> Maybe XReply+getReply elem = do+ childElem <- unqual "reply" `findChild` elem+ let fields = mapMaybe structField $ elChildren childElem+ guard $ not $ null fields+ return fields++xevent :: Element -> Parse XDecl+xevent elem = do+ name <- elem `attr` "name"+ number <- elem `attr` "number" >>= readM+ fields <- mapAlt structField $ elChildren elem+ guard $ not $ null fields+ return $ XEvent name number fields++xevcopy :: Element -> Parse XDecl+xevcopy elem = do+ name <- elem `attr` "name"+ number <- elem `attr` "number" >>= readM+ ref <- elem `attr` "ref"+ -- do we have a qualified ref?+ let (mname,evname) = splitRef ref+ details <- lookupEvent mname evname + return $ XEvent name number $ case details of+ Nothing -> error $ "Unresolved event: " ++ show mname ++ " " ++ ref+ Just (EventDetails _ _ x) -> x++-- we need to do string processing to distinguish qualified from+-- unqualified types.+mkType :: String -> Type+mkType str =+ let (mname, name) = splitRef str+ in case mname of+ Just mod -> QualType mod name+ Nothing -> UnQualType name++splitRef :: Name -> (Maybe Name, Name)+splitRef ref = case split ':' ref of+ (x,"") -> (Nothing, x)+ (a, b) -> (Just a, b)++-- |Neither returned string contains the first occurance of the+-- supplied Char.+split :: Char -> String -> (String, String)+split c xs = go xs+ where go [] = ([],[])+ go (x:xs) | x == c = ([],xs)+ | otherwise = + let (lefts, rights) = go xs+ in (x:lefts,rights)+ ++xerror :: Element -> Parse XDecl+xerror elem = do+ name <- elem `attr` "name"+ number <- elem `attr` "number" >>= readM+ fields <- mapAlt structField $ elChildren elem+ guard $ not $ null fields+ return $ XError name number fields+++xercopy :: Element -> Parse XDecl+xercopy elem = do+ name <- elem `attr` "name"+ number <- elem `attr` "number" >>= readM+ ref <- elem `attr` "ref"+ let (mname, ername) = splitRef ref+ details <- lookupError mname ername+ return $ XError name number $ case details of+ Nothing -> error $ "Unresolved error: " ++ show mname ++ " " ++ ref+ Just (ErrorDetails _ _ x) -> x++xstruct :: Element -> Parse XDecl+xstruct elem = do+ name <- elem `attr` "name"+ fields <- mapAlt structField $ elChildren elem+ guard $ not $ null fields+ return $ XStruct name fields++xunion :: Element -> Parse XDecl+xunion elem = do+ name <- elem `attr` "name"+ fields <- mapAlt structField $ elChildren elem+ guard $ not $ null fields+ return $ XUnion name fields++xidtype :: Element -> Parse XDecl+xidtype elem = liftM XidType $ elem `attr` "name"++xidunion :: Element -> Parse XDecl+xidunion elem = do+ name <- elem `attr` "name"+ let types = mapMaybe xidUnionElem $ elChildren elem+ guard $ not $ null types+ return $ XidUnion name types++xidUnionElem :: Element -> Maybe XidUnionElem+xidUnionElem elem = do+ guard $ elem `named` "type"+ return $ XidUnionElem $ mkType $ strContent elem++xtypedef :: Element -> Parse XDecl+xtypedef elem = do+ oldtyp <- liftM mkType $ elem `attr` "oldname"+ newname <- elem `attr` "newname"+ return $ XTypeDef newname oldtyp+++structField :: MonadPlus m => Element -> m StructElem+structField elem+ | elem `named` "field" = do+ typ <- liftM mkType $ elem `attr` "type"+ name <- elem `attr` "name"+ return $ SField name typ++ | elem `named` "pad" = do+ bytes <- elem `attr` "bytes" >>= readM+ return $ Pad bytes++ | elem `named` "list" = do+ typ <- liftM mkType $ elem `attr` "type"+ name <- elem `attr` "name"+ let expr = firstChild elem >>= expression+ return $ List name typ expr++ | elem `named` "valueparam" = do+ mask_typ <- liftM mkType $ elem `attr` "value-mask-type"+ mask_name <- elem `attr` "value-mask-name"+ list_name <- elem `attr` "value-list-name"+ return $ ValueParam mask_typ mask_name list_name++ | elem `named` "exprfield" = do+ typ <- liftM mkType $ elem `attr` "type"+ name <- elem `attr` "name"+ expr <- firstChild elem >>= expression+ return $ ExprField name typ expr++ | elem `named` "reply" = fail "" -- handled separate++ | otherwise = let name = elName elem+ in error $ "I don't know what to do with structelem "+ ++ show name++expression :: MonadPlus m => Element -> m Expression+expression elem | elem `named` "fieldref"+ = return $ FieldRef $ strContent elem+ | elem `named` "value"+ = Value `liftM` readM (strContent elem)+ | elem `named` "bit"+ = Bit `liftM` do+ n <- readM (strContent elem)+ guard $ n >= 0+ return n+ | elem `named` "op" = do+ binop <- elem `attr` "op" >>= toBinop+ [exprLhs,exprRhs] <- mapM expression $ elChildren elem+ return $ Op binop exprLhs exprRhs+++toBinop :: MonadPlus m => String -> m Binop+toBinop "+" = return Add+toBinop "-" = return Sub+toBinop "*" = return Mult+toBinop "/" = return Div+toBinop "&" = return And+toBinop "&" = return And+toBinop ">>" = return RShift+toBinop _ = mzero+++++----+----+-- Utility functions+----+----++firstChild :: MonadPlus m => Element -> m Element+firstChild = listToM . elChildren++listToM :: MonadPlus m => [a] -> m a+listToM [] = mzero+listToM (x:xs) = return x++named :: Element -> String -> Bool+named (Element qname _ _ _) name | qname == unqual name = True+named _ _ = False++attr :: MonadPlus m => Element -> String -> m String+(Element _ xs _ _) `attr` name = case List.find p xs of+ Just (Attr _ res) -> return res+ _ -> mzero+ where p (Attr qname _) | qname == unqual name = True+ p _ = False++-- adapted from Network.CGI.Protocol+readM :: (MonadPlus m, Read a) => String -> m a+readM = liftM fst . listToM . reads++maybeRead :: Read a => String -> Maybe a+maybeRead = readM++-- In retorspect maybe I should've gone with MonadPlus, but the+-- code is already written so I guess I need these instances.++instance (Alternative f, Monad f) => Alternative (ReaderT r f) where+ empty = ReaderT $ \r -> empty+ m1 <|> m2 = ReaderT $ \r -> (runReaderT m1 r) <|> (runReaderT m2 r)++instance (Applicative f, Monad f) => Applicative (ReaderT r f) where+ pure a = ReaderT $ \_ -> pure a+ mf <*> m = ReaderT $ \r -> (runReaderT mf r) <*> (runReaderT m r)
+ Data/XCB/Pretty.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE TypeSynonymInstances #-}++-- |+-- Module : Data.XCB.Pretty+-- Copyright : (c) Antoine Latter 2008+-- License : BSD3+--+-- Maintainer: Antoine Latter <aslatter@gmail.com>+-- Stability : provisional+-- Portability: portable - requires TypeSynonymInstances+--+-- Pretty-printers for the tyes declared in this package.+-- This does NOT ouput XML - it produces human-readable information+-- intended to aid in debugging.+module Data.XCB.Pretty where++import Data.XCB.Types++import Text.PrettyPrint.HughesPJ++-- |Minimal complete definition:+--+-- One of 'pretty' or 'toDoc'.+class Pretty a where+ toDoc :: a -> Doc+ pretty :: a -> String++ pretty = show . toDoc+ toDoc = text . pretty++-- Builtin types++instance Pretty String where+ pretty = show++instance Pretty Int where+ pretty = show++instance Pretty a => Pretty (Maybe a) where+ toDoc Nothing = empty+ toDoc (Just a) = toDoc a++ pretty Nothing = ""+ pretty (Just a) = pretty a++-- Simple stuff++instance Pretty XidUnionElem where+ pretty = show++instance Pretty Binop where+ pretty Add = "+"+ pretty Sub = "-"+ pretty Mult = "*"+ pretty Div = "/"+ pretty RShift = ">>"+ pretty And = "&"++instance Pretty EnumElem where+ toDoc (EnumElem name expr)+ = text name <> char ':' <+> toDoc expr++instance Pretty Type where+ toDoc (UnQualType name) = text name+ toDoc (QualType mod name) = text mod <> char '.' <> text name++-- More complex stuff++instance Pretty Expression where+ toDoc (Value n) = toDoc n+ toDoc (Bit n) = text "2^" <> toDoc n+ toDoc (FieldRef ref) = char '$' <> text ref+ toDoc (Op binop exprL exprR)+ = parens $ hsep [toDoc exprL+ ,toDoc binop+ ,toDoc exprR+ ]++instance Pretty StructElem where+ toDoc (Pad n) = braces $ toDoc n <+> text "bytes"+ toDoc (List nm typ len)+ = text nm <+> text "::" <+> brackets (toDoc typ) <+> toDoc len+ toDoc (SField nm typ) = hsep [text nm+ ,text "::"+ ,toDoc typ+ ]+ toDoc (ExprField nm typ expr)+ = parens (text nm <+> text "::" <+> toDoc typ)+ <+> toDoc expr+ toDoc (ValueParam typ mname lname)+ = text "Valueparam" <+>+ text "::" <+>+ hsep (punctuate (char ',') [toDoc typ+ ,text mname+ ,text lname+ ])++instance Pretty XDecl where+ toDoc (XStruct nm elems) =+ hang (text "Struct:" <+> text nm) 2 $ vcat $ map toDoc elems+ toDoc (XTypeDef nm typ) = hsep [text "TypeDef:"+ ,text nm+ ,text "as"+ ,toDoc typ+ ]+ toDoc (XEvent nm n elems) =+ hang (text "Event:" <+> text nm <> char ',' <> toDoc n) 2 $+ vcat $ map toDoc elems+ toDoc (XRequest nm n elems mrep) = + (hang (text "Request:" <+> text nm <> char ',' <> toDoc n) 2 $+ vcat $ map toDoc elems)+ $$ case mrep of+ Nothing -> empty+ Just reply ->+ hang (text "Reply:" <+> text nm <> char ',' <> toDoc n) 2 $+ vcat $ map toDoc reply+ toDoc (XidType nm) = text "XID:" <+> text nm+ toDoc (XidUnion nm elems) = + hang (text "XID" <+> text "Union:" <+> text nm) 2 $+ vcat $ map toDoc elems+ toDoc (XEnum nm elems) =+ hang (text "Enum:" <+> text nm) 2 $ vcat $ map toDoc elems+ toDoc (XUnion nm elems) = + hang (text "Union:" <+> text nm) 2 $ vcat $ map toDoc elems+ toDoc (XImport nm) = text "Import:" <+> text nm+ toDoc (XError nm n elems) = + hang (text "Error:" <+> text nm) 2 $ vcat $ map toDoc elems++instance Pretty XHeader where+ toDoc xhd = text (xheader_header xhd) $$+ (vcat $ map toDoc (xheader_decls xhd))
+ Data/XCB/Types.hs view
@@ -0,0 +1,99 @@++-- |+-- Module : Data.XCB.Types+-- Copyright : (c) Antoine Latter 2008+-- License : BSD3+--+-- Maintainer: Antoine Latter <aslatter@gmail.com>+-- Stability : provisional+-- Portability: portable+--+-- Defines types inteneded to be equivalent to the schema used by+-- the XCB project in their XML protocol description.+--+++module Data.XCB.Types where+++import qualified Data.List as L+import Control.Monad++-- 'xheader_header' is the name gauranteed to exist, and is used in+-- imports and in type qualifiers.+--+-- 'xheader_name' is the InterCaps name, and should be prefered in the naming+-- of types, functions and haskell modules when available.+-- |This is what a single XML file maps to. It contains some meta-data+-- then declarations.+data XHeader = XHeader {xheader_header :: Name -- ^Name of module. Used in the other modules as a reference.+ ,xheader_xname :: Maybe Name -- ^Name used to indentify extensions between the X client and server.+ ,xheader_name :: Maybe Name -- ^InterCaps name.+ ,xheader_multiword :: Maybe Bool+ ,xheader_major_version :: Maybe Int+ ,xheader_minor_version :: Maybe Int+ ,xheader_decls :: [XDecl] -- ^Declarations contained in this module.+ }+ deriving (Show)++-- |The different types of declarations which can be made in one of the+-- XML files.+data XDecl = XStruct Name [StructElem]+ | XTypeDef Name Type+ | XEvent Name Int [StructElem]+ | XRequest Name Int [StructElem] (Maybe XReply)+ | XidType Name+ | XidUnion Name [XidUnionElem]+ | XEnum Name [EnumElem]+ | XUnion Name [StructElem]+ | XImport Name+ | XError Name Int [StructElem]+ deriving (Show)++data StructElem = Pad Int+ | List Name Type (Maybe Expression)+ | SField Name Type+ | ExprField Name Type Expression+ | ValueParam Type MaskName ListName+ deriving (Show)++type Name = String+type XReply = [StructElem]+type Ref = String++-- |Types may include a reference to the containing module.+data Type = UnQualType Name+ | QualType Name Name+ deriving Show++type MaskName = Name+type ListName = Name++data ExInfo = ExInfo Name Name Version+ deriving (Show)++type Version = (String,String)++data XidUnionElem = XidUnionElem Type+ deriving (Show)++-- Should only ever have expressions of type 'Value' or 'Bit'.+data EnumElem = EnumElem Name (Maybe Expression)+ deriving (Show)++-- |Declarations may contain expressions from this small language+data Expression = Value Int -- ^A literal value+ | Bit Int -- ^A log-base-2 literal value+ | FieldRef String -- ^A reference to a field in the same declaration+ | Op Binop Expression Expression -- ^A binary opeation+ deriving (Show)++-- |Supported Binary operations.+data Binop = Add+ | Sub+ | Mult+ | Div+ | And+ | RShift+ deriving (Show)+
+ Data/XCB/Utils.hs view
@@ -0,0 +1,18 @@+module Data.XCB.Utils where++-- random utility functions++import Data.Char+import Control.Applicative++ensureUpper :: String -> String+ensureUpper [] = []+ensureUpper (x:xs) = (toUpper x) : xs++-- |Like mapMaybe, but for any Alternative.+-- Never returns 'empty', instead returns 'pure []'+mapAlt :: Alternative f => (a -> f b) -> [a] -> f [b]+mapAlt f xs = go xs+ where go [] = pure []+ go (y:ys) = pure (:) <*> f y <*> go ys+ <|> go ys
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Antoine Latter 2008++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of the author nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ xcb-types.cabal view
@@ -0,0 +1,37 @@+Name: xcb-types+Version: 0.1.0+Cabal-Version: >= 1.2+Synopsis: Parses XML files used by the XCB project+Description: This package provides types which mirror the structures+ used in the XCB code generation XML files.++ See project http://xcb.freedesktop.org/ for more information about the XCB+ project.++ The XML files describe the data-types, events and requests used by the+ X Protocol, and are used to auto-generate large parts of the XCB project.++ This package parses these XML files into Haskell data structures.++ If you want to do something with these XML descriptions but don't want+ to learn XSLT, this package should help.++License: BSD3+License-file: LICENSE+Author: Antoine Latter+Maintainer: Antoine Latter <aslatter@gmail.com>++Build-type: Simple++Category: Data++Library++ Build-depends: base, xml, pretty, mtl+ Exposed-modules: Data.XCB,+ Data.XCB.Types,+ Data.XCB.Pretty,+ Data.XCB.FromXML++ Other-modules: Data.XCB.Utils+