HGL-3.1: Graphics/SOE.hs
-----------------------------------------------------------------------------
-- |
-- Module : Graphics.SOE
-- Copyright : (c) Alastair Reid, 1999-2003
-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer : libraries@haskell.org
-- Stability : stable
-- Portability : non-portable (requires concurrency)
--
-- The graphics library used in /The Haskell School of Expression/,
-- by Paul Hudak, cf <http://www.haskell.org/soe/>.
--
-- /Notes:/
--
-- * This module is called @SOEGraphics@ in the book. It is a cut
-- down version of "Graphics.HGL", with the interface frozen to match
-- the book.
--
-- * In chapters 13, 17 and 19 of the book, there are imports of modules
-- @Win32Misc@ and @Word@. These should be omitted, as 'timeGetTime'
-- and 'word32ToInt' are provided by this module.
-----------------------------------------------------------------------------
module Graphics.SOE
(
-- * Getting started
runGraphics -- p41
-- * Windows
, Title -- p40
, Size
, Window
, openWindow
, getWindowSize -- not in SOE, but Resize is
, clearWindow -- used on p127
, drawInWindow -- p41
, drawInWindowNow -- backward compatibility (p281)
, setGraphic -- p168
, closeWindow -- p41
-- ** General windows
, openWindowEx -- p168
, RedrawMode -- SOE has (Graphic -> DrawFun)
, drawGraphic -- p168
, drawBufferedGraphic
-- * Drawing
, Graphic -- p41
, emptyGraphic -- p171
, overGraphic
, overGraphics -- not in SOE, but an obvious extension
-- ** Color
, Color(..) -- p43
, withColor
-- ** Drawing text
, text -- p41
-- ** Drawing shapes
, Point
, ellipse -- p43
, shearEllipse
, line
, polygon
, polyline
, polyBezier -- warning: becomes error message and polyline in X11
, Angle -- not in SOE
, arc -- not in SOE, but handy for pie charts
-- ** Regions
, Region -- p117
, createRectangle
, createEllipse
, createPolygon
, andRegion
, orRegion
, xorRegion
, diffRegion
, drawRegion
-- * User interaction
-- ** Keyboard events
, getKey -- p41
-- ** Mouse events
, getLBP -- used on p127
, getRBP -- not in SOE, but obvious
-- ** General events
, Event(..) -- p214
, maybeGetWindowEvent -- p248
, getWindowEvent -- not in SOE, but obvious
-- * Time
-- Timers that tick at regular intervals are set up by 'openWindowEx'.
, Word32 -- p168
, getWindowTick
, timeGetTime -- from Win32
, word32ToInt -- obsolete function from Data.Word
) where
import Graphics.HGL
hiding (getKey, getKeyEx, openWindowEx,
Event(..), getWindowEvent, maybeGetWindowEvent)
import qualified Graphics.HGL as HGL
import Control.Monad(liftM)
import Data.Word(Word32)
----------------------------------------------------------------
-- Interface
----------------------------------------------------------------
-- | A rectangular region, with the given points as opposite corners.
createRectangle :: Point -> Point -> Region
-- | A polygonal region defined by a list of 'Point's.
createPolygon :: [Point] -> Region
-- | An elliptical region that fits in the rectangle with the given points
-- as opposite corners.
createEllipse :: Point -> Point -> Region
-- | The union of two regions.
orRegion :: Region -> Region -> Region
-- | The intersection of two regions.
andRegion :: Region -> Region -> Region
-- | The part of the first region that is not also in the second.
diffRegion :: Region -> Region -> Region
-- | Draw a 'Region' in the current color.
drawRegion :: Region -> Graphic
-- | Another name for 'drawInWindow', retained for backwards compatibility.
drawInWindowNow :: Window -> Graphic -> IO ()
----------------------------------------------------------------
-- Implementation
----------------------------------------------------------------
-- | an extended version of 'openWindow'.
openWindowEx :: Title -- ^ the title of the window
-> Maybe Point -- ^ the initial position of the window
-> Maybe Size -- ^ the initial size of the window
-> RedrawMode -- ^ how to display a graphic on the window
-> Maybe Word32 -- ^ optionally attach a timer to the window,
-- with the specified time (in milliseconds)
-- between ticks.
-> IO Window
openWindowEx a b (Just c) d e =
HGL.openWindowEx a b c d (fmap fromIntegral e)
openWindowEx a b Nothing d e =
HGL.openWindowEx a b (300,300) d (fmap fromIntegral e)
createRectangle = rectangleRegion
createEllipse = ellipseRegion
createPolygon = polygonRegion
orRegion = unionRegion
andRegion = intersectRegion
diffRegion = subtractRegion
drawRegion = regionToGraphic
-- backwards compatibility:
-- | Draw directly to the window
-- (slightly faster than 'drawBufferedGraphic', but more prone to flicker).
drawGraphic :: RedrawMode
drawGraphic = Unbuffered
-- | Use a /double buffer/ to reduce flicker and thus improve the look
-- of animations.
drawBufferedGraphic :: RedrawMode
drawBufferedGraphic = DoubleBuffered
-- should have a different way to specify background color
-- drawBufferedGraphicBC :: RGB -> RedrawMode
drawInWindowNow = drawInWindow
-- | The current time of day (in milliseconds).
timeGetTime :: IO Word32
timeGetTime = liftM integerToWord32 getTime
integerToWord32 :: Integer -> Word32
#ifdef __GLASGOW_HASKELL__
integerToWord32 = fromInteger -- conversion to Word32 doesn't overflow
#else
integerToWord32 n = fromInteger (n `mod` (toInteger (maxBound::Word32) + 1))
#endif
-- | An obsolete special case of 'fromIntegral'.
word32ToInt :: Word32 -> Int
word32ToInt = fromIntegral
----------------------------------------------------------------
-- Event, getKey, and maybeGetWindowEvent compatibility
----------------------------------------------------------------
{-
The SOE sources are set in stone, so this module provides the interface
SOE expects, even if the Graphics library moves on (cf. Event.Key).
-}
-- Deprecated SOE compatibility.
-- | Wait until a key is pressed and released,
-- and return the corresponding character.
getKey :: Window -> IO Char
getKey w = do { getKeyEx w True; getKeyEx w False }
-- | Wait until a key is pressed (if the second argument is 'True')
-- or released (otherwise), and return the corresponding character.
-- (not in SOE)
getKeyEx :: Window -> Bool -> IO Char
getKeyEx w down = loop
where
loop = do
e <- HGL.getWindowEvent w
case e of
HGL.Key { HGL.keysym = k, HGL.isDown = isDown }
| isDown == down && isCharKey k
-> return (keyToChar k)
_ -> loop
-- | Wait for the next event in the window.
getWindowEvent :: Window -> IO Event
getWindowEvent w = liftM toSOEEvent (HGL.getWindowEvent w)
-- | Return a pending eventin the window, if any.
maybeGetWindowEvent :: Window -> IO (Maybe Event)
maybeGetWindowEvent w = liftM (fmap toSOEEvent) (HGL.maybeGetWindowEvent w)
-- tiresome, but necessary.
toSOEEvent :: HGL.Event -> Event
toSOEEvent (HGL.Char x) = Key x True
toSOEEvent (HGL.Key k isDown) = Key (keyToChar k) isDown
toSOEEvent (HGL.Button pt left down) = Button pt left down
toSOEEvent (HGL.MouseMove p) = MouseMove p
toSOEEvent (HGL.Resize) = Resize
toSOEEvent (HGL.Closed) = Closed
-- | User interface events
data Event
= Key
{ char :: Char -- ^ character corresponding to the key
, isDown :: Bool -- ^ if 'True', the key was pressed;
-- otherwise it was released
} -- ^ occurs when a key was pressed or released.
| Button
{ pt :: Point -- ^ the position of the mouse cursor
, isLeft :: Bool -- ^ if 'True', it was the left button
, isDown :: Bool -- ^ if 'True', the button was pressed;
-- otherwise it was released
} -- ^ occurs when a mouse button is pressed or released.
| MouseMove
{ pt :: Point -- ^ the position of the mouse cursor
} -- ^ occurs when the mouse is moved inside the window.
| Resize -- ^ occurs when the window is resized.
-- The new window size can be discovered using
-- 'getWindowSize'.
| Closed -- ^ occurs when the window is closed.
deriving Show
----------------------------------------------------------------
-- End
----------------------------------------------------------------