module Graphics.X11.World ( World, openWorld, setCursorPos, getCursorPos, setCursorDir, getCursorDir, setCursorSize, setCursorShape, drawWorld, closeWorld, withEvent, flushWorld, eventToChar, isDeleteEvent, makeFilledPolygonCursor, lineToBG, cleanBG, getWindowSize, Event(..), Position, Dimension, Point(..), testModuleWorld ) where import Graphics.X11 import Graphics.X11.Xlib.Extras import Data.IORef import Data.Bits import Data.Char import Data.Convertible import Control.Monad.Tools data World = World{ wDisplay :: Display, wWindow :: Window, wGC :: GC, wDel :: Atom, wBG :: Pixmap, wBuf :: Pixmap, wPos :: IORef (Double, Double), wDir :: IORef Double, wSize :: IORef Double, wShape :: IORef (World -> Double -> Double -> Double -> Double -> IO ()) } getWindowSize :: World -> IO (Double, Double) getWindowSize w = do (_, _, _, width, height, _, _) <- getGeometry (wDisplay w) (wWindow w) return (fromIntegral width, fromIntegral height) setCursorPos :: World -> Double -> Double -> IO () setCursorPos w x y = writeIORef (wPos w) (x, y) getCursorPos :: World -> IO (Double, Double) getCursorPos w = readIORef (wPos w) setCursorDir :: World -> Double -> IO () setCursorDir w d = writeIORef (wDir w) d getCursorDir :: World -> IO Double getCursorDir w = readIORef (wDir w) setCursorSize :: World -> Double -> IO () setCursorSize w s = writeIORef (wSize w) s setCursorShape :: World -> (World -> Double -> Double -> Double -> Double -> IO ()) -> IO () setCursorShape w s = writeIORef (wShape w) s testModuleWorld :: IO () testModuleWorld = main main :: IO () main = do putStrLn "module World" w <- openWorld withEvent w () $ \() ev -> case ev of ExposeEvent{} -> return ((), True) KeyEvent{} -> do ch <- eventToChar w ev return ((), ch /= 'q') ClientMessageEvent{} -> return ((), not $ isDeleteEvent w ev) _ -> error $ "not implemented for event " ++ show ev closeWorld w openWorld :: IO World openWorld = do dpy <- openDisplay "" del <- internAtom dpy "WM_DELETE_WINDOW" True let scr = defaultScreen dpy root <- rootWindow dpy scr (_, _, _, width, height, _, _) <- getGeometry dpy root let black = blackPixel dpy scr white = whitePixel dpy scr win <- createSimpleWindow dpy root 0 0 width height 1 black white bg <- createPixmap dpy root width height $ defaultDepth dpy scr buf <- createPixmap dpy root width height $ defaultDepth dpy scr gc <- createGC dpy win gc' <- createGC dpy win setForeground dpy gc' 0xffffff fillRectangle dpy bg gc' 0 0 width height fillRectangle dpy buf gc' 0 0 width height setWMProtocols dpy win [del] selectInput dpy win $ exposureMask .|. keyPressMask mapWindow dpy win flush dpy initPos <- newIORef undefined initDir <- newIORef undefined initSize <- newIORef undefined initShape <- newIORef undefined return $ World dpy win gc del bg buf initPos initDir initSize initShape closeWorld :: World -> IO () closeWorld = closeDisplay . wDisplay drawWorld :: World -> IO () drawWorld w = do (_, _, _, width, height, _, _) <- getGeometry (wDisplay w) (wWindow w) copyArea (wDisplay w) (wBG w) (wBuf w) (wGC w) 0 0 width height 0 0 (x, y) <- readIORef $ wPos w d <- readIORef $ wDir w s <- readIORef $ wSize w displayCursor <- readIORef $ wShape w displayCursor w s d x y copyArea (wDisplay w) (wBuf w) (wWindow w) (wGC w) 0 0 width height 0 0 withEvent :: World -> s -> (s -> Event -> IO (s, Bool)) -> IO s withEvent w stat0 act = doWhile stat0 $ \stat -> allocaXEvent $ \e -> do nextEvent (wDisplay w) e getEvent e >>= act stat eventToChar :: World -> Event -> IO Char eventToChar w ev = fmap (chr . fromEnum) $ keycodeToKeysym (wDisplay w) (ev_keycode ev) 0 isDeleteEvent :: World -> Event -> Bool isDeleteEvent w ev@ClientMessageEvent{} = convert (head $ ev_data ev) == wDel w isDeleteEvent _ _ = False makeFilledPolygonCursor :: World -> [Point] -> IO () makeFilledPolygonCursor w ps = fillPolygon (wDisplay w) (wBuf w) (wGC w) ps nonconvex coordModeOrigin lineToBG :: World -> Position -> Position -> Position -> Position -> IO () lineToBG w x1 y1 x2 y2 = drawLine (wDisplay w) (wBG w) (wGC w) x1 y1 x2 y2 cleanBG :: World -> IO () cleanBG w = do (_, _, _, width, height, _, _) <- getGeometry (wDisplay w) (wWindow w) gc <- createGC (wDisplay w) (wWindow w) setForeground (wDisplay w) gc 0xffffff fillRectangle (wDisplay w) (wBG w) gc 0 0 width height flushWorld :: World -> IO () flushWorld = flush . wDisplay