    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.HGL.Window
    -- Copyright   :  (c) Alastair Reid, 1999-2003
    -- License     :  BSD-style (see the file libraries/base/LICENSE)
    --
    -- Maintainer  :  libraries@haskell.org
    -- Stability   :  provisional
    -- Portability :  non-portable (requires concurrency)
    --
    -- Windows in a simple graphics library.
    --
    -----------------------------------------------------------------------------

    module Graphics.HGL.Window
    	(
    	-- * Windows
    	  Window
    	, Title			-- = String
    	, RedrawMode(Unbuffered, DoubleBuffered)
    	, openWindowEx		-- :: Title -> Maybe Point -> Maybe Size ->
    				--    RedrawMode -> Maybe Time -> IO Window
    	, getWindowRect		-- :: Window -> IO (Point,Point)
    	, closeWindow		-- :: Window -> IO ()

    	-- * Drawing in a window
    	, setGraphic		-- :: Window -> Graphic -> IO ()
    	, getGraphic		-- :: Window -> IO Graphic
    	, modGraphic		-- :: Window -> (Graphic -> Graphic) -> IO ()
    	, directDraw		-- :: Window -> Graphic -> IO ()
    	-- not in X11: , redrawWindow		-- :: Window -> IO ()

    	-- * Events in a window
    	, Event(..)
    	-- , Event(Char,Key,Button,MouseMove,Resize,Closed) -- deriving(Show)
    	-- , char		-- :: Event -> Char
    	-- , keysym		-- :: Event -> Key
    	-- , isDown		-- :: Event -> Bool
    	-- , pt			-- :: Event -> Point
    	-- , isLeft		-- :: Event -> Bool
    	, getWindowEvent	-- :: Window -> IO Event
    	, maybeGetWindowEvent	-- :: Window -> IO (Maybe Event)

    	-- * Timer ticks
    	-- | Timers that tick at regular intervals are set up by 'openWindowEx'.
    	, getWindowTick		-- :: Window -> IO ()
    	, getTime		-- :: IO Time
    	) where

    #ifdef __HADDOCK__
    import Graphics.HGL.Key
    #endif
    import Graphics.HGL.Units
    import Graphics.HGL.Draw( Graphic )
    import Graphics.HGL.Internals.Event( Event(..) )
    import Graphics.HGL.Internals.Types( Title, RedrawMode(..), getTime )
    import qualified Graphics.HGL.Internals.Events as E
    import Graphics.HGL.Internals.Utilities( modMVar, modMVar_ )
    #if !X_DISPLAY_MISSING
    import Graphics.HGL.X11.Window (Window(..))
    import qualified Graphics.HGL.X11.Window as X (openWindowEx, closeWindow,
    	redrawWindow, directDraw, getWindowRect )
    #else
    import Graphics.HGL.Win32.WND
    	(WND, openWND, getHWND, closeWND, wndRect, redrawWND, drawWND)
    import Graphics.HGL.Win32.Types
    import Graphics.HGL.Win32.Draw(
    	drawGraphic, drawBufferedGraphic
    	)
    import Graphics.HGL.Draw	(Draw)
    -- import Graphics.HGL.Internals.Types

    import qualified Graphics.Win32 as Win32
    #endif

    import Control.Concurrent.MVar

    ----------------------------------------------------------------
    -- Interface
    ----------------------------------------------------------------

    -- | Wait for the next event on the given window.
    getWindowEvent      :: Window -> IO Event

    -- | Check for a pending event on the given window.
    maybeGetWindowEvent :: Window -> IO (Maybe Event)

    -- | Wait for the next tick event from the timer on the given window.
    getWindowTick       :: Window -> IO ()

    -- | Get the current drawing in a window.
    getGraphic :: Window -> IO Graphic

    -- | Set the current drawing in a window.
    setGraphic :: Window -> Graphic -> IO ()

    -- | Update the drawing for a window.
    -- Note that this does not force a redraw.
    modGraphic :: Window -> (Graphic -> Graphic) -> IO ()

    -- | General window creation.
    openWindowEx
      :: Title		-- ^ title of the window
      -> Maybe Point	-- ^ the optional initial position of a window
      -> Size		-- ^ initial size of the window
      -> RedrawMode		-- ^ how to display a graphic on the window
      -> Maybe Time		-- ^ the time between ticks (in milliseconds) of an
    			-- optional timer associated with the window
      -> IO Window

    -- | Close the window.
    closeWindow   :: Window -> IO ()

    redrawWindow  :: Window -> IO ()

    directDraw    :: Window -> Graphic -> IO ()

    -- | The position of the top left corner of the window on the screen,
    -- and the size of the window.
    getWindowRect :: Window -> IO (Point, Size)

    ----------------------------------------------------------------
    -- Implementation
    ----------------------------------------------------------------

    getWindowEvent w     = E.getEvent (events w)

    maybeGetWindowEvent w
      = do noEvent <- E.isNoEvent (events w)
           if noEvent
              then return Nothing
              else do ev <- getWindowEvent w
                      return (Just ev)

    getWindowTick w      = E.getTick (events w)

    getGraphic w = readMVar (graphic w)

    setGraphic w p = do
      modMVar (graphic w) (const p)
      redrawWindow w

    modGraphic w = modMVar_ (graphic w)

    #if !X_DISPLAY_MISSING

    openWindowEx    = X.openWindowEx
    closeWindow     = X.closeWindow
    getWindowRect   = X.getWindowRect
    redrawWindow    = X.redrawWindow
    directDraw      = X.directDraw

    #else /* X_DISPLAY_MISSING */

    data Window = MkWindow {
    	events  :: E.Events,	    -- the event stream
    	graphic :: MVar (Draw ()),  -- the current graphic
    	wnd     :: WND	    	    -- the real window
    	}

    openWindowEx name pos size redrawMode tickRate = do
    	graphic <- newMVar (return ())
    	events  <- E.newEvents
    	let draw = \ hwnd hdc -> do
                          p <- readMVar graphic
    		      repaint p hwnd hdc
    	wnd     <- openWND name (fmap fromPoint pos) (Just $ fromPoint size)
    			   events draw (fmap fromInteger tickRate)
    	mkWindow wnd events graphic
     where
      repaint = case redrawMode of
                Unbuffered     -> drawGraphic
                DoubleBuffered -> drawBufferedGraphic

    mkWindow       :: WND -> E.Events -> MVar (Draw ()) -> IO Window
    mkWindow wnd events graphic = do
    	return (MkWindow { wnd=wnd, events=events, graphic=graphic })

    closeWindow w   = closeWND  (wnd w)
    getWindowRect w = wndRect   (wnd w)
    redrawWindow w  = redrawWND (wnd w)
    directDraw w p  = drawWND   (wnd w) p

    -- in case you need low level access
    windowHWND     :: Window -> IO Win32.HWND
    windowHWND w    = getHWND   (wnd w)

    #endif /* X_DISPLAY_MISSING */

    ----------------------------------------------------------------
    -- End
    ----------------------------------------------------------------