packages feed

starrover2-0.0.9: src/SDLUtils.hs

module SDLUtils
where

import Control.Exception (throwIO)
import Data.Maybe
import Data.Char
import Data.List

import Graphics.UI.SDL as SDL
import qualified Graphics.UI.SDL.Utilities as SDLU
import qualified Data.Edison.Assoc.StandardMap as M

-- generic stuff
pollAllSDLEvents :: IO [SDL.Event]
pollAllSDLEvents = pollAllSDLEvents'' True

pollAllSDLEvents' :: IO [SDL.Event]
pollAllSDLEvents' = pollAllSDLEvents'' False

pollAllSDLEvents'' :: Bool -> IO [SDL.Event]
pollAllSDLEvents'' throwOnQuit = go []
    where go l = do
            e <- SDL.pollEvent
            if e == SDL.NoEvent
              then return l
              else if throwOnQuit && e == SDL.Quit
                     then (throwIO $ userError "User wants to quit")
                     else do
                       es <- pollAllSDLEvents
                       return (e:es)

hasEvent :: (SDL.Event -> Bool) -> [SDL.Event] -> Bool
hasEvent fun evts = or $ map fun evts

getSDLChar :: IO SDLKey
getSDLChar = do
  e <- waitEvent
  case e of
    KeyDown (Keysym n _ _) -> return n
    _                      -> getSDLChar

getSpecificSDLChar :: SDLKey -> IO ()
getSpecificSDLChar c = do
  d <- getSDLChar
  if c == d
    then return ()
    else getSpecificSDLChar c

getSpecificSDLChars :: [SDLKey] -> IO SDLKey
getSpecificSDLChars cs = do
  d <- getSDLChar
  if d `elem` cs
    then return d
    else getSpecificSDLChars cs

keyDowns :: [SDL.Event] -> [SDLKey]
keyDowns = foldl' (\acc e -> case e of KeyDown (Keysym n _ _) -> (n:acc); _ -> acc) []

specificKeyPressed :: [SDLKey] -> [SDL.Event] -> Maybe SDLKey
specificKeyPressed ks evts = listToMaybe $ intersect ks (keyDowns evts)

processEvent :: (Monad m) => [(SDLKey, (m (), m ()))] -> Event -> m ()
processEvent n evt =
  let mk = case evt of
             KeyDown (Keysym k _ _) -> Just (True, k)
             KeyUp   (Keysym k _ _) -> Just (False, k) 
             _                      -> Nothing
  in case mk of
       Nothing     -> return ()
       Just (e, k) -> case lookup k n of
                        Nothing       -> return ()
                        Just (a1, a2) -> if e then a1 else a2

processEvents
  :: (Monad m) => [(SDLKey, (m (), m ()))] -> [Event] -> m ()
processEvents n = mapM_ (processEvent n)

isQuit :: [SDL.Event] -> Bool
isQuit = hasEvent isq
  where isq Quit = True
        isq (KeyDown (Keysym SDLK_q _ _)) = True
        isq _ = False

keyWasPressed :: SDLKey -> [SDL.Event] -> Bool
keyWasPressed j es = j `elem` keyDowns es

oneofKeyWasPressed :: [SDLKey] -> [SDL.Event] -> Bool
oneofKeyWasPressed j es = not . null $ j `intersect` keyDowns es

anyKeyOrMouseWasPressed :: [SDL.Event] -> Bool
anyKeyOrMouseWasPressed = hasEvent isk
  where isk (KeyDown _)             = True
        isk (MouseButtonDown _ _ _) = True
        isk _                       = False

anyKeyOrMouseWasPressedIO :: IO Bool
anyKeyOrMouseWasPressedIO = pollAllSDLEvents >>= return . anyKeyOrMouseWasPressed

inputLine :: Bool -> [SDL.Event] -> String -> (String, Bool)
inputLine shift es sd = (sd', entered)
  where sd'      = osd ++ newInput
        osd      = reverse . drop backspaces $ reverse sd
        backspaces = Data.List.length $ filter (== SDLK_BACKSPACE) keys
        keys     = keyDowns es
        entered  = keyWasPressed SDLK_RETURN es
        newInput = map up $ catMaybes $ map sdlkKeyToChar keys
        up       = if shift then toUpper else id

shiftDown :: IO Bool
shiftDown = do
  mods <- getModState
  return (KeyModLeftShift `elem` mods || KeyModRightShift `elem` mods || KeyModShift `elem` mods)

-- TODO: add special characters
sdlkKeyToChar :: SDLKey -> Maybe Char
sdlkKeyToChar = Prelude.flip M.lookupM (M.fromSeq 
    (zip (SDLU.enumFromTo SDLK_a SDLK_z ++ SDLU.enumFromTo SDLK_0 SDLK_9 ++ [SDLK_SPACE]) 
         (['a'..'z'] ++ ['0'..'9'] ++ " ")))

mouseClickIn :: Int -> [SDL.MouseButton] -> ((Int, Int), (Int, Int)) -> [SDL.Event] -> Bool
mouseClickIn height buttons ((minx, miny), (diffx, diffy)) =
  hasEvent f
    where f (MouseButtonDown x y b) = 
            let x' = fromIntegral x
                y' = height - fromIntegral y
            in b `elem` buttons && 
               x' >= minx && 
               y' >= miny && 
               x' <= minx + diffx && 
               y' <= miny + diffy
          f _ = False

mouseClickInAny :: Int -> [SDL.MouseButton] -> [((Int, Int), (Int, Int))] -> [SDL.Event] -> Maybe ((Int, Int), (Int, Int))
mouseClickInAny height bs areas events = 
  foldr (\x acc -> if mouseClickIn height bs x events then Just x else acc) 
        Nothing areas