----------------------------------------------------------------------------- -- | -- 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 . -- -- /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 ----------------------------------------------------------------