{-# LANGUAGE CPP #-}
module Graphics.GL.GetProcAddress (
getProcAddress,
getProcAddressWithSuffixes,
getExtension,
getProcAddressChecked,
getProcAddressWithSuffixesChecked,
getExtensionChecked,
getVersion, version,
getExtensions, extensions
) where
#if !MIN_VERSION_base(4,8,0)
import Data.Functor( (<$>), (<$) )
#endif
import Control.Monad ( forM )
import Control.Monad.IO.Class ( MonadIO(..) )
import Data.ByteString.Unsafe ( unsafePackCString, unsafeUseAsCString )
import Data.Char ( isDigit )
import Data.Set ( Set, fromList )
import Data.Text ( pack, unpack )
import Data.Text.Encoding ( encodeUtf8, decodeUtf8 )
import Foreign.C.String ( CString )
import Foreign.Marshal.Alloc ( alloca )
import Foreign.Marshal.Error ( throwIf )
import Foreign.Ptr ( Ptr, nullPtr, castPtr, FunPtr, nullFunPtr )
import Foreign.Storable ( peek )
import Graphics.GL.Tokens
import Graphics.GL.Types
import System.IO.Unsafe ( unsafePerformIO )
import Text.ParserCombinators.ReadP
getProcAddress :: MonadIO m => String -> m (FunPtr a)
getProcAddress cmd = liftIO $ withUtf8String cmd hs_OpenGLRaw_getProcAddress
foreign import ccall unsafe "hs_OpenGLRaw_getProcAddress"
hs_OpenGLRaw_getProcAddress :: CString -> IO (FunPtr a)
getProcAddressChecked :: MonadIO m => String -> m (FunPtr a)
getProcAddressChecked cmd = liftIO $ check cmd $ getProcAddress cmd
getProcAddressWithSuffixes :: MonadIO m => String -> [String] -> m (FunPtr a)
getProcAddressWithSuffixes _ [] = return nullFunPtr
getProcAddressWithSuffixes cmd (x:xs) = do
p <- getProcAddress (cmd ++ x)
if p == nullFunPtr
then getProcAddressWithSuffixes cmd xs
else return p
getProcAddressWithSuffixesChecked :: MonadIO m
=> String -> [String] -> m (FunPtr a)
getProcAddressWithSuffixesChecked cmd suffixes =
liftIO $ check cmd $ getProcAddressWithSuffixes cmd suffixes
getExtension :: MonadIO m => String -> m (FunPtr a)
getExtension cmd = liftIO $ getProcAddressWithSuffixes cmd vendorSuffixes
getExtensionChecked :: MonadIO m => String -> m (FunPtr a)
getExtensionChecked cmd =
liftIO $ getProcAddressWithSuffixesChecked cmd vendorSuffixes
check :: String -> IO (FunPtr a) -> IO (FunPtr a)
check = throwIfNullFunPtr . ("unknown OpenGL command " ++)
throwIfNullFunPtr :: String -> IO (FunPtr a) -> IO (FunPtr a)
throwIfNullFunPtr = throwIf (== nullFunPtr) . const
vendorSuffixes :: [String]
vendorSuffixes = [
"",
"ARB", "KHR", "OES",
"EXT",
"NV", "SGIX", "AMD", "APPLE", "ATI", "SGIS", "ANGLE", "QCOM", "IMG", "SUN",
"IBM", "ARM", "MESA", "INTEL", "HP", "SGI", "OML", "INGR", "3DFX", "WIN",
"PGI", "NVX", "GREMEDY", "DMP", "VIV", "SUNX", "S3", "REND", "MESAX", "FJ",
"ANDROID" ]
getExtensions :: MonadIO m => m (Set String)
getExtensions = liftIO $ Data.Set.fromList <$> do
getString <- makeGetString
v <- getVersionWith getString
if v >= (3, 0)
then do getInteger <- makeGetInteger
getStringi <- makeGetStringi
numExtensions <- getInteger GL_NUM_EXTENSIONS
forM [ 0 .. fromIntegral numExtensions - 1 ] $
getStringi GL_EXTENSIONS
else words <$> getString GL_EXTENSIONS
getVersion :: MonadIO m => m (Int, Int)
getVersion = liftIO $ makeGetString >>= getVersionWith
getVersionWith :: (GLenum -> IO String) -> IO (Int, Int)
getVersionWith getString =
runParser parseVersion (-1, -1) <$> getString GL_VERSION
runParser :: ReadP a -> a -> String -> a
runParser parser failed str =
case readP_to_S parser str of
[(v, "")] -> v
_ -> failed
parseVersion :: ReadP (Int, Int)
parseVersion = do
_prefix <-
("CL" <$ string "OpenGL ES-CL ") <++
("CM" <$ string "OpenGL ES-CM ") <++
("ES" <$ string "OpenGL ES " ) <++
("GL" <$ string "" )
major <- read <$> munch1 isDigit
minor <- char '.' >> read <$> munch1 isDigit
_release <- (char '.' >> munch1 (/= ' ')) <++ return ""
_vendorStuff <- (char ' ' >> get `manyTill` eof) <++ ("" <$ eof)
return (major, minor)
makeGetString :: IO (GLenum -> IO String)
makeGetString = do
glGetString_ <- dynGLenumIOPtrGLubyte <$> getProcAddress "glGetString"
return $ \name -> glGetString_ name >>= peekGLstring
foreign import CALLCONV "dynamic" dynGLenumIOPtrGLubyte
:: FunPtr (GLenum -> IO (Ptr GLubyte))
-> GLenum -> IO (Ptr GLubyte)
makeGetStringi :: IO (GLenum -> GLuint -> IO String)
makeGetStringi = do
glGetStringi_ <- dynGLenumGLuintIOPtrGLubyte <$> getProcAddress "glGetStringi"
return $ \name index -> glGetStringi_ name index >>= peekGLstring
foreign import CALLCONV "dynamic" dynGLenumGLuintIOPtrGLubyte
:: FunPtr (GLenum -> GLuint -> IO (Ptr GLubyte))
-> GLenum -> GLuint -> IO (Ptr GLubyte)
makeGetInteger :: IO (GLenum -> IO GLint)
makeGetInteger = do
glGetIntegerv_ <- dynGLenumPtrGLintIOVoid <$> getProcAddress "glGetIntegerv"
return $ \name -> alloca $ \p -> glGetIntegerv_ name p >> peek p
foreign import CALLCONV "dynamic" dynGLenumPtrGLintIOVoid
:: FunPtr (GLenum -> Ptr GLint -> IO ())
-> GLenum -> Ptr GLint -> IO ()
peekGLstring :: Ptr GLubyte -> IO String
peekGLstring = ptr (return "") (peekUtf8String . castPtr)
ptr :: b -> (Ptr a -> b) -> Ptr a -> b
ptr n f p | p == nullPtr = n
| otherwise = f p
withUtf8String :: String -> (CString -> IO a) -> IO a
withUtf8String = unsafeUseAsCString . encodeUtf8 . pack . (++ "\0")
peekUtf8String :: CString -> IO String
peekUtf8String p = unpack . decodeUtf8 <$> unsafePackCString p
extensions :: Set String
extensions = unsafePerformIO getExtensions
{-# NOINLINE extensions #-}
version :: (Int, Int)
version = unsafePerformIO getVersion
{-# NOINLINE version #-}