xturtle 0.0.12 → 0.0.13
raw patch · 7 files changed
+563/−437 lines, 7 filesdep +X11-xftdep +setlocaledep +x11-xim
Dependencies added: X11-xft, setlocale, x11-xim
Files
- src/Graphics/X11/Turtle.hs +13/−12
- src/Graphics/X11/TurtleField.hs +470/−0
- src/Graphics/X11/TurtleInput.hs +12/−6
- src/Graphics/X11/TurtleMove.hs +19/−11
- src/Graphics/X11/TurtleState.hs +45/−4
- src/Graphics/X11/WindowLayers.hs +0/−401
- xturtle.cabal +4/−3
src/Graphics/X11/Turtle.hs view
@@ -49,16 +49,18 @@ windowWidth, windowHeight, onclick,+ onkeypress, waitField, - xturtleVersion+ xturtleVersion,+ write ) where import Graphics.X11.TurtleMove( Field, Layer, Character, forkIOX, openField, closeField, addCharacter, addLayer, fieldSize, clearLayer, clearCharacter,- addThread, fieldColor, onclick, waitField,+ addThread, fieldColor, onclick, onkeypress, waitField, Color(..), moveTurtle ) import Graphics.X11.TurtleInput(@@ -72,11 +74,10 @@ import Prelude hiding(Left) import Data.IORef(IORef, newIORef, readIORef, modifyIORef) import Data.Bits(shift, (.|.))-import Data.Word(Word8) import Data.Fixed(mod') xturtleVersion :: (Int, String)-xturtleVersion = (21, "0.0.12")+xturtleVersion = (24, "0.0.13") data Turtle = Turtle { field :: Field,@@ -172,17 +173,14 @@ penup = flip sendCommand Penup pendown = flip sendCommand Pendown -pencolor :: Turtle -> Word8 -> Word8 -> Word8 -> IO ()-pencolor t r_ g_ b_ = sendCommand t $ Pencolor c- where- c = shift r 16 .|. shift g 8 .|. b- [r, g, b] = map fromIntegral [r_, g_, b_]+pencolor :: Turtle -> Double -> Double -> Double -> IO ()+pencolor t r_ g_ b_ = sendCommand t $ Pencolor r_ g_ b_ -- c -bgcolor :: Field -> Word8 -> Word8 -> Word8 -> IO ()-bgcolor f r_ g_ b_ = fieldColor f c+bgcolor :: Field -> Double -> Double -> Double -> IO ()+bgcolor f r_ g_ b_ = fieldColor f $ Color c where c = shift r 16 .|. shift g 8 .|. b- [r, g, b] = map fromIntegral [r_, g_, b_]+ [r, g, b] = map (round . (* 255)) [r_, g_, b_] pensize :: Turtle -> Double -> IO () pensize t = sendCommand t . Pensize@@ -192,6 +190,9 @@ radians :: Turtle -> IO () radians = flip degrees $ 2 * pi++write :: Turtle -> String -> Double -> String -> IO ()+write t fnt sz = sendCommand t . Write fnt sz undo :: Turtle -> IO () undo t = readIORef (stateIndex t)
+ src/Graphics/X11/TurtleField.hs view
@@ -0,0 +1,470 @@+module Graphics.X11.TurtleField(+ Field,+ Layer,+ Character,++ openField,+ closeField,+ waitField,+ fieldColor,+ fieldSize,++ addLayer,+ addCharacter,++ drawLine,+ drawLineNotFlush,+ writeString,+ drawCharacter,+ drawCharacterAndLine,+ clearCharacter,++ undoLayer,+ clearLayer,+ flushLayer,++ onclick,+ onkeypress,++ forkIOX,+ addThread,++ Color(..)+) where++import Graphics.X11(+ Display, Window, Pixmap, Atom, GC, Point(..), Dimension, Position, Pixel,++ setLineAttributes, lineSolid, capRound, joinRound,++ openDisplay, closeDisplay, flush, defaultScreen, rootWindow,+ whitePixel, blackPixel, defaultDepth,+ createSimpleWindow, mapWindow, createPixmap, internAtom, createGC,++ setForeground, copyArea,+ fillRectangle, fillPolygon, nonconvex, coordModeOrigin,++ setWMProtocols, selectInput, allocaXEvent, nextEvent, XEventPtr,+ keyPressMask, exposureMask, buttonPressMask,++ getGeometry, initThreads, connectionNumber, pending, destroyWindow,++ defaultVisual, defaultColormap, defaultScreenOfDisplay,++ supportsLocale, setLocaleModifiers,+ xK_VoidSymbol+ )+import qualified Graphics.X11 as X (drawLine)+import Graphics.X11.Xlib.Extras(Event(..), getEvent)+import Graphics.X11.Xft+import Graphics.X11.Xrender+import Graphics.X11.Xim++import Data.IORef(IORef, newIORef, readIORef, writeIORef, modifyIORef)+import Data.Bits((.|.))+import Data.Convertible(convert)+import Data.List.Tools(modifyAt, setAt)+import Data.Bool.Tools(whether)+import Data.Maybe++import Control.Monad(replicateM, forM_, forever, replicateM_, when, unless)+import Control.Monad.Tools(doWhile_, whenM, unlessM)+import Control.Arrow((***))+import Control.Concurrent(+ forkIO, ThreadId, Chan, newChan, writeChan, readChan, threadWaitRead,+ killThread)++import System.Posix.Types+import System.Locale.SetLocale+import Foreign.C.Types++data Color = Color {pixel :: Pixel}++colorToPixel :: Color -> Pixel+colorToPixel = pixel++data Field = Field{+ fDisplay :: Display,+ fWindow :: Window,+ fGC :: GC,+ fGCBG :: GC,+ fDel :: Atom,+ fUndoBuf :: Pixmap,+ fBG :: Pixmap,+ fBuf :: Pixmap,+ fWidth :: IORef Dimension,+ fHeight :: IORef Dimension,+ fBuffed :: IORef [IO ()],+ fLayers :: IORef [[Bool -> IO ()]],+ fCharacters :: IORef [IO ()],+ fWait :: Chan (),+ fEvent :: Chan (Maybe Event),+ fClose :: Chan (),+ fClosed :: IORef Bool,+ fRunning :: IORef [ThreadId],+ fOnclick :: IORef (Double -> Double -> IO Bool),+ fKeypress :: IORef (Char -> IO Bool),+ fEnd :: Chan ()+ }++data Layer = Layer{+ layerField :: Field,+ layerId :: Int+ }++data Character = Character{+ characterField :: Field,+ characterId :: Int+ }++openField :: IO Field+openField = do+ setLocale LC_CTYPE Nothing >>= maybe (error "Can't set locale.") print+ _ <- initThreads+ unlessM supportsLocale $ error "Current locale is notSupported."+ _ <- setLocaleModifiers ""+ dpy <- openDisplay ""+ del <- internAtom dpy "WM_DELETE_WINDOW" True+ let scr = defaultScreen dpy+ root <- rootWindow dpy scr+ (_, _, _, rWidth, rHeight, _, _) <- getGeometry dpy root+ let black = blackPixel dpy scr+ white = whitePixel dpy scr+ depth = defaultDepth dpy scr+ bufs <- replicateM 3 $ createPixmap dpy root rWidth rHeight depth+ win <- createSimpleWindow dpy root 0 0 rWidth rHeight 1 black white+ im <- openIM dpy Nothing Nothing Nothing+ ic <- createIC im [XIMPreeditNothing, XIMStatusNothing] win+ fevent <- getICValue ic "filterEvents"+ [gc, gcBG] <- replicateM 2 $ createGC dpy win+ setForeground dpy gcBG 0xffffff+ forM_ bufs $ \bf -> fillRectangle dpy bf gcBG 0 0 rWidth rHeight+ setWMProtocols dpy win [del]+ selectInput dpy win $ exposureMask .|. keyPressMask .|. buttonPressMask .|. fevent+ mapWindow dpy win+ [widthRef, heightRef] <- mapM newIORef [rWidth, rHeight]+ buffActions <- newIORef []+ layerActions <- newIORef []+ characterActions <- newIORef []+ wait <- newChan+ event <- newChan+ close <- newChan+ closed <- newIORef False+ running <- newIORef []+ onclickRef <- newIORef $ const $ const $ return True+ keypressRef <- newIORef $ const $ return True+ endRef <- newChan+ writeChan wait ()+ let f = Field{+ fDisplay = dpy,+ fWindow = win,+ fGC = gc,+ fGCBG = gcBG,+ fDel = del,+ fUndoBuf = head bufs,+ fBG = bufs !! 1,+ fBuf = bufs !! 2,+ fWidth = widthRef,+ fHeight = heightRef,+ fBuffed = buffActions,+ fLayers = layerActions,+ fCharacters = characterActions,+ fWait = wait,+ fEvent = event,+ fClose = close,+ fClosed = closed,+ fRunning = running,+ fOnclick = onclickRef,+ fKeypress = keypressRef,+ fEnd = endRef+ }+ _ <- forkIOX $ runLoop ic f+ flushWindow f+ return f++runLoop :: XIC -> Field -> IO ()+runLoop ic f = allocaXEvent $ \e -> do+ endc <- waitInput f+ th1 <- forkIOX $ forever $ do+ evN <- pending $ fDisplay f+ replicateM_ (fromIntegral evN) $ do+ nextNotFilteredEvent (fDisplay f) e+ ev <- getEvent e+ writeChan (fEvent f) $ Just ev+ end <- readChan endc+ when end $ writeChan (fEvent f) Nothing+ (>> (closeDisplay (fDisplay f) >> writeChan (fEnd f) ())) $+ (>> destroyWindow (fDisplay f) (fWindow f)) $ doWhile_ $ do+ mev <- readChan $ fEvent f+ case mev of+ Just (ExposeEvent{}) -> do+ (_, _, _, width, height, _, _) <-+ getGeometry (fDisplay f) (fWindow f)+ writeIORef (fWidth f) width+ writeIORef (fHeight f) height+ redrawAll f+ return True+ Just (KeyEvent{}) -> do+ (mstr, mks) <- utf8LookupString ic e+ let str = fromMaybe " " mstr+ _ks = fromMaybe xK_VoidSymbol mks+ readIORef (fKeypress f) >>= fmap and . ($ str) . mapM+ Just ev@ButtonEvent{} -> do+ pos <- convertPosRev f (ev_x ev) (ev_y ev)+ readIORef (fOnclick f) >>= ($ pos) . uncurry+ Just ev@ClientMessageEvent{} ->+ return $ convert (head $ ev_data ev) /= fDel f+ Nothing -> killThread th1 >> return False+ _ -> return True++onclick :: Field -> (Double -> Double -> IO Bool) -> IO ()+onclick f = writeIORef $ fOnclick f++onkeypress :: Field -> (Char -> IO Bool) -> IO ()+onkeypress f = writeIORef $ fKeypress f++fieldColor :: Field -> Color -> IO ()+fieldColor f clr = do+ setForeground (fDisplay f) (fGCBG f) $ colorToPixel clr+ let bufs = [fUndoBuf f, fBG f, fBuf f]+ width <- readIORef $ fWidth f+ height <- readIORef $ fHeight f+ forM_ bufs $ \bf -> fillRectangle (fDisplay f) bf (fGCBG f) 0 0 width height+ redrawAll f++getConnection :: Field -> Fd+getConnection = Fd . connectionNumber . fDisplay++waitInput :: Field -> IO (Chan Bool)+waitInput f = do+ c <- newChan+ _ <- forkIOX $ forever $ do+ threadWaitRead $ getConnection f+ writeChan c False+ _ <- forkIO $ do+ readChan $ fClose f+ writeChan c True+ return c++closeField :: Field -> IO ()+closeField f = do+ readIORef (fRunning f) >>= mapM_ killThread+ writeChan (fClose f) ()+ writeIORef (fClosed f) True++addThread :: Field -> ThreadId -> IO ()+addThread f tid = modifyIORef (fRunning f) (tid :)++flushLayer :: Layer -> IO ()+flushLayer = flushWindow . layerField++addLayer :: Field -> IO Layer+addLayer f = do+ ls <- readIORef $ fLayers f+ writeIORef (fLayers f) (ls ++ [[]])+ modifyIORef (fBuffed f) (++ [return ()])+ return Layer{layerField = f, layerId = length ls}++addCharacter :: Field -> IO Character+addCharacter f = do+ cs <- readIORef $ fCharacters f+ writeIORef (fCharacters f) (cs ++ [return ()])+ return Character{characterField = f, characterId = length cs}++runIfOpened :: Field -> IO a -> IO ()+runIfOpened f act = do+ cl <- readIORef $ fClosed f+ unless cl $ act >> return ()++drawLineNotFlush ::+ Layer -> Double -> Color -> Double -> Double -> Double -> Double -> IO ()+drawLineNotFlush l@Layer{layerField = f} lw_ clr x1 y1 x2 y2 = runIfOpened f $ do+ drawLineBuf f lw clr fBG x1 y1 x2 y2+ addLayerAction l $ whether+ (drawLineBuf f lw clr fUndoBuf x1 y1 x2 y2)+ (drawLineBuf f lw clr fBG x1 y1 x2 y2)+ where+ lw = round lw_++drawLine :: Layer -> Double -> Color -> Double -> Double -> Double -> Double -> IO ()+drawLine l@Layer{layerField = f} lw_ clr x1 y1 x2 y2 = runIfOpened f $ do+ drawLineBuf f lw clr fBG x1 y1 x2 y2 >> redrawCharacters f+ addLayerAction l $ whether+ (drawLineBuf f lw clr fUndoBuf x1 y1 x2 y2)+ (drawLineBuf f lw clr fBG x1 y1 x2 y2)+ where+ lw = round lw_++writeString :: Layer -> String -> Double -> Double -> Double -> Double ->+ Double -> Double -> String -> IO ()+writeString l@Layer{layerField = f} fname size r g b x y str = do+ writeStringBuf f fBG fname size r g b x y str+ redrawCharacters f+ addLayerAction l $ whether+ (writeStringBuf f fUndoBuf fname size r g b x y str)+ (writeStringBuf f fBG fname size r g b x y str)++writeStringBuf :: Field -> (Field -> Pixmap) -> String -> Double -> Double -> Double -> Double ->+ Double -> Double -> String -> IO ()+writeStringBuf f buf fname size r g b x_ y_ str = do+ let dpy = fDisplay f+ scr = defaultScreen dpy+ scrN = defaultScreenOfDisplay dpy+ visual = defaultVisual dpy scr+ colormap = defaultColormap dpy scr+ xftDraw <- xftDrawCreate dpy (buf f) visual colormap+ xftFont <- xftFontOpen dpy scrN $ fname ++ "-" ++ show (round size :: Int) -- "KochiGothic-20"+ [(x, y)] <- convertPos f [(x_, y_)]+ withXftColorValue dpy visual colormap color $ \c ->+ xftDrawString xftDraw c xftFont x y str+ where+ color = XRenderColor {+ xrendercolor_red = round $ r * 0xffff,+ xrendercolor_green = round $ b * 0xffff,+ xrendercolor_blue = round $ g * 0xffff,+ xrendercolor_alpha = 0xffff+ }++clearCharacter :: Character -> IO ()+clearCharacter c = runIfOpened (characterField c) $+ setCharacter c $ return ()++drawCharacter :: Character -> Color -> [(Double, Double)] -> IO ()+drawCharacter c@Character{characterField = f} clr sh =+ runIfOpened (characterField c) $ setCharacter c $ do+ setForeground (fDisplay f) (fGC f) $ colorToPixel clr+ fillPolygonBuf (characterField c) sh++drawCharacterAndLine :: Character -> Color -> [(Double, Double)] -> Double ->+ Double -> Double -> Double -> Double -> IO ()+drawCharacterAndLine c@Character{characterField = f} clr ps lw_ x1 y1 x2 y2 =+ runIfOpened f $ setCharacter c $ do+ setForeground (fDisplay f) (fGC f) $ colorToPixel clr+ fillPolygonBuf f ps >> drawLineBuf f lw clr fBuf x1 y1 x2 y2+ where+ lw = round lw_++undoLayer :: Layer -> IO Bool+undoLayer Layer{layerField = f, layerId = lid} = do+ ls <- readIORef $ fLayers f+ if null $ ls !! lid then return False else do+ writeIORef (fLayers f) $ modifyAt ls lid init+ redraw f+ return True++clearLayer :: Layer -> IO ()+clearLayer Layer{layerField = f, layerId = lid} = do+ ls <- readIORef $ fLayers f+ writeIORef (fLayers f) $ setAt ls lid []+ buffed <- readIORef $ fBuffed f+ writeIORef (fBuffed f) $ setAt buffed lid $ return ()+ redrawBuf f+ redraw f++forkIOX :: IO () -> IO ThreadId+forkIOX = (initThreads >>) . forkIO++--------------------------------------------------------------------------------++undoN :: Int+undoN = 100++addLayerAction :: Layer -> (Bool -> IO ()) -> IO ()+addLayerAction Layer{layerField = f, layerId = lid} act = do+ ls <- readIORef $ fLayers f+ if length (ls !! lid) > undoN+ then do head (ls !! lid) True+ buffed <- readIORef $ fBuffed f+ writeIORef (fBuffed f) $ + modifyAt buffed lid (>> head (ls !! lid) True)+ writeIORef (fLayers f) $+ modifyAt ls lid $ (++ [act]) . tail+ else writeIORef (fLayers f) $ modifyAt ls lid (++ [act])++setCharacter :: Character -> IO () -> IO ()+setCharacter Character{characterField = f, characterId = cid} act = do+ cs <- readIORef $ fCharacters f+ writeIORef (fCharacters f) $ setAt cs cid act+ redrawCharacters f+ flushWindow f++fillPolygonBuf :: Field -> [(Double, Double)] -> IO ()+fillPolygonBuf f ps_ = do+ ps <- convertPos f ps_+ fillPolygon (fDisplay f) (fBuf f) (fGC f) (map (uncurry Point) ps)+ nonconvex coordModeOrigin++drawLineBuf :: Field -> Int -> Color -> (Field -> Pixmap) ->+ Double -> Double -> Double -> Double -> IO ()+drawLineBuf f@Field{fDisplay = dpy, fGC = gc} lw clr bf x1_ y1_ x2_ y2_ = do+ setForeground (fDisplay f) (fGC f) $ colorToPixel clr+ setLineAttributes (fDisplay f) (fGC f) (fromIntegral lw) lineSolid capRound joinRound+ [(x1, y1), (x2, y2)] <- convertPos f [(x1_, y1_), (x2_, y2_)]+ X.drawLine dpy (bf f) gc x1 y1 x2 y2++convertPos :: Field -> [(Double, Double)] -> IO [(Position, Position)]+convertPos f ps = do+ (width, height) <- fieldSize f+ return $ (round . (+ width / 2) *** round . (+ height / 2) . negate)+ `map` ps++convertPosRev :: Field -> CInt -> CInt -> IO (Double, Double)+convertPosRev f x y = do+ (width, height) <- fieldSize f+ return (fromIntegral x - width / 2, fromIntegral (- y) + height / 2)++fieldSize :: Field -> IO (Double, Double)+fieldSize w = fmap (fromIntegral *** fromIntegral) $ winSize w++winSize :: Field -> IO (Dimension, Dimension)+winSize f = do+ width <- readIORef $ fWidth f+ height <- readIORef $ fHeight f+ return (width, height)++redrawAll :: Field -> IO ()+redrawAll f = do+ redrawBuf f+ redraw f+ flushWindow f++redrawBuf :: Field -> IO ()+redrawBuf f = do+ winSize f >>=+ uncurry (fillRectangle (fDisplay f) (fUndoBuf f) (fGCBG f) 0 0)+ readIORef (fBuffed f) >>= sequence_++redraw :: Field -> IO ()+redraw = withLock $ \f -> do+ (width, height) <- winSize f+ copyArea (fDisplay f) (fUndoBuf f) (fBG f) (fGC f) 0 0 width height 0 0+ readIORef (fLayers f) >>= mapM_ ($ False) . concat+ copyArea (fDisplay f) (fBG f) (fBuf f) (fGC f) 0 0 width height 0 0+ readIORef (fCharacters f) >>= sequence_++redrawCharacters :: Field -> IO ()+redrawCharacters = withLock $ \f -> do+ (width, height) <- winSize f+ copyArea (fDisplay f) (fBG f) (fBuf f) (fGC f) 0 0 width height 0 0+ readIORef (fCharacters f) >>= sequence_++flushWindow :: Field -> IO ()+flushWindow = withLock $ \f -> do+ (width, height) <- winSize f+ copyArea (fDisplay f) (fBuf f) (fWindow f) (fGC f) 0 0 width height 0 0+ flush $ fDisplay f++withLock :: (Field -> IO a) -> Field -> IO a+withLock act f = do+ readChan $ fWait f+ ret <- act f+ writeChan (fWait f) ()+ return ret++waitField :: Field -> IO ()+waitField = readChan . fEnd++nextNotFilteredEvent :: Display -> XEventPtr -> IO ()+nextNotFilteredEvent dpy e = do+ nextEvent dpy e+ whenM (filterEvent e 0) $ nextNotFilteredEvent dpy e
src/Graphics/X11/TurtleInput.hs view
@@ -11,7 +11,8 @@ degrees ) where -import Graphics.X11.TurtleState(TurtleState(..), initialTurtleState, Color)+import Graphics.X11.TurtleState(TurtleState(..), initialTurtleState, pencolor,+ Draw(..)) import Control.Concurrent.Chan(Chan, newChan, getChanContents) import Prelude hiding(Left) @@ -34,9 +35,10 @@ | Forward Double | Left Double | Undonum Int- | Pencolor Color+ | Pencolor Double Double Double | Pensize Double | Degrees Double+ | Write String Double String deriving Show getTurtleStates :: [(Double, Double)] -> IO (Chan TurtleInput, [TurtleState])@@ -50,24 +52,28 @@ nextTurtle t (Shape sh) = (clearState t){shape = sh} nextTurtle t (ShapeSize ss) = (clearState t){shapesize = ss} nextTurtle t (Goto x y) = (clearState t){position = (x, y), line = pendown t,- drawed = if pendown t then (pencolor t, pensize t, position t, (x, y))- : drawed t else drawed t} + drawed = if pendown t+ then Line (pencolor t) (pensize t) (position t) (x, y) : drawed t else drawed t} nextTurtle t (Rotate d) = (clearState t){direction = d} nextTurtle t Pendown = (clearState t){pendown = True} nextTurtle t Penup = (clearState t){pendown = False} nextTurtle t (SetVisible v) = (clearState t){visible = v} nextTurtle t (Undonum un) = (clearState t){undonum = un} nextTurtle t (Clear) = (clearState t){clear = True, drawed = []}-nextTurtle t (Pencolor c) = (clearState t){pencolor = c}+nextTurtle t (Pencolor r g b) = (clearState t){red = r, green = g, blue = b}-- setPencolor (clearState t) c nextTurtle t (Pensize ps) = (clearState t){pensize = ps} nextTurtle t (Degrees ds) = (clearState t){ degrees = ds, direction = direction t * ds / degrees t }+nextTurtle t (Write fnt sz str) = (clearState t){+ draw = Str (red t, green t, blue t) fnt sz (position t) str,+ drawed = Str (red t, green t, blue t) fnt sz (position t) str : drawed t+ } nextTurtle _ _ = error "not defined" clearState :: TurtleState -> TurtleState-clearState t = t{line = False, undo = False, undonum = 1, clear = False}+clearState t = t{line = False, undo = False, undonum = 1, clear = False, draw = NoDraw} inputToTurtle :: [TurtleState] -> TurtleState -> [TurtleInput] -> [TurtleState] inputToTurtle [] ts0 (Undo : tis) = ts0 : inputToTurtle [] ts0 tis
src/Graphics/X11/TurtleMove.hs view
@@ -14,20 +14,23 @@ addThread, fieldColor, onclick,+ onkeypress, waitField,+ writeString,+ Color(..), moveTurtle ) where -import Graphics.X11.TurtleState(TurtleState(..), Color)-import Graphics.X11.WindowLayers(+import Graphics.X11.TurtleState(TurtleState(..), pencolor, Draw(..))+import Graphics.X11.TurtleField( Field, Layer, Character, forkIOX, openField, closeField, flushLayer, addLayer, addCharacter, fieldSize, clearLayer, drawLine, drawCharacter, drawCharacterAndLine, undoLayer, drawLineNotFlush, clearCharacter, addThread,- fieldColor, onclick, waitField+ fieldColor, onkeypress, onclick, waitField, writeString, Color(..) ) import Control.Concurrent(threadDelay)@@ -53,34 +56,39 @@ moveTurtle :: Character -> Layer -> TurtleState -> TurtleState -> IO () moveTurtle c l t0 t1 = do- when (undo t1 && line t0) $ do+ when (undo t1 && (line t0 || draw t0 /= NoDraw)) $ do done <- undoLayer l unless done $ clearLayer l >> drawLines l (drawed t1) when (undo t1 && clear t0) $ drawLines l $ drawed t1 when (visible t1) $ do forM_ (getDirections (dir t0) (dir t1)) $ \d -> do- drawTurtle c (pencolor t1) (shape t1) (shapesize t1) d+ drawTurtle c (Color $ pencolor t1) (shape t1) (shapesize t1) d (pensize t1) p0 Nothing threadDelay rotateSpeed forM_ (getPositions x0 y0 x1 y1) $ \p -> do- drawTurtle c (pencolor t1) (shape t1) (shapesize t1)+ drawTurtle c (Color $ pencolor t1) (shape t1) (shapesize t1) (dir t1) (pensize t1) p lineOrigin threadDelay moveSpeed- drawTurtle c (pencolor t1) (shape t1) (shapesize t1) (dir t1)+ drawTurtle c (Color $ pencolor t1) (shape t1) (shapesize t1) (dir t1) (pensize t1) p1 lineOrigin unless (visible t1) $ clearCharacter c when (not (undo t1) && line t1) $- drawLine l (pensize t1) (pencolor t1) x0 y0 x1 y1 >> flushLayer l+ drawLine l (pensize t1) (Color $ pencolor t1) x0 y0 x1 y1 >> flushLayer l when (clear t1) $ clearLayer l >> flushLayer l+ unless (undo t1) $ drawDraw l (draw t1) >> flushLayer l where (tl, to) = if undo t1 then (t0, t1) else (t1, t0) lineOrigin = if line tl then Just $ position to else Nothing p0@(x0, y0) = position t0 p1@(x1, y1) = position t1 -drawLines :: Layer -> [(Color, Double, (Double, Double), (Double, Double))] -> IO ()-drawLines l ls =- mapM_ (\(clr, lw, (x0, y0), (x1, y1)) -> drawLineNotFlush l lw clr x0 y0 x1 y1) $ reverse ls+drawLines :: Layer -> [Draw] -> IO ()+drawLines l = mapM_ (drawDraw l) . reverse++drawDraw :: Layer -> Draw -> IO ()+drawDraw _ NoDraw = return ()+drawDraw l (Line clr lw (x0, y0) (x1, y1)) = drawLineNotFlush l lw (Color clr) x0 y0 x1 y1+drawDraw l (Str (r, g, b) fnt sz (x, y) str) = writeString l fnt sz r g b x y str getPositions :: Double -> Double -> Double -> Double -> [Pos] getPositions x0 y0 x1 y1 = take num $ zip [x0, x0 + dx .. ] [y0, y0 + dy .. ]
src/Graphics/X11/TurtleState.hs view
@@ -1,20 +1,57 @@ module Graphics.X11.TurtleState ( TurtleState(..), initialTurtleState,- Color+ Color,+ pencolor,+ setPencolor,+ Draw(..) ) where import Data.Word(Word32)+import Data.Bits type Color = Word32 +data Draw+ = NoDraw+ | Line{+ color :: Color,+ width :: Double,+ begin :: (Double, Double),+ end :: (Double, Double)+ }+ | Str{+ scolor :: (Double, Double, Double),+ font :: String,+ size :: Double,+ pos :: (Double, Double),+ contents :: String+ }+ deriving (Show, Eq)++pencolor :: TurtleState -> Word32+pencolor t = c+ where+ c = shift (round $ red t * 0xff) 16 .|.+ shift (round $ green t * 0xff) 8 .|. round (blue t * 0xff)++setPencolor :: TurtleState -> Word32 -> TurtleState+setPencolor t c = t{red = r, green = g, blue = b}+ where+ r_ = shiftR c 16 .&. 0xff+ g_ = shiftR c 8 .&. 0xff+ b_ = c .&. 0xff+ [r, g, b] = map ((/ 0xff) . fromIntegral) [r_, g_, b_]+ data TurtleState = TurtleState { position :: (Double, Double), direction :: Double, degrees :: Double, pendown :: Bool, pensize :: Double,- pencolor :: Color,+ red :: Double,+ green :: Double,+ blue :: Double, shape :: [(Double, Double)], shapesize :: Double, visible :: Bool,@@ -22,7 +59,8 @@ undo :: Bool, line :: Bool, undonum :: Int,- drawed :: [(Color, Double, (Double, Double), (Double, Double))]+ draw :: Draw,+ drawed :: [Draw] } deriving Show initialTurtleState :: [(Double, Double)] -> TurtleState@@ -32,7 +70,9 @@ degrees = 360, pendown = True, pensize = 0,- pencolor = 0x000000,+ red = 0,+ green = 0,+ blue = 0, shape = sh, shapesize = 1, visible = True,@@ -40,5 +80,6 @@ undo = False, line = False, undonum = 1,+ draw = NoDraw, drawed = [] }
− src/Graphics/X11/WindowLayers.hs
@@ -1,401 +0,0 @@-module Graphics.X11.WindowLayers(- Field,- Layer,- Character,-- openField,- closeField,- waitField,- fieldColor,- fieldSize,-- addLayer,- addCharacter,-- drawLine,- drawLineNotFlush,- drawCharacter,- drawCharacterAndLine,- clearCharacter,-- undoLayer,- clearLayer,- flushLayer,-- onclick,-- forkIOX,- addThread-) where--import Graphics.X11(- Display, Window, Pixmap, Atom, GC, Point(..), Dimension, Position, Pixel,-- setLineAttributes, lineSolid, capRound, joinRound,-- openDisplay, closeDisplay, flush, defaultScreen, rootWindow,- whitePixel, blackPixel, defaultDepth,- createSimpleWindow, mapWindow, createPixmap, internAtom, createGC,-- setForeground, copyArea,- fillRectangle, fillPolygon, nonconvex, coordModeOrigin,-- setWMProtocols, selectInput, allocaXEvent, nextEvent,- keyPressMask, exposureMask, buttonPressMask,-- getGeometry, initThreads, connectionNumber, pending, destroyWindow- )-import qualified Graphics.X11 as X (drawLine)-import Graphics.X11.Xlib.Extras(Event(..), getEvent)--import Data.IORef(IORef, newIORef, readIORef, writeIORef, modifyIORef)-import Data.Bits((.|.))-import Data.Convertible(convert)-import Data.List.Tools(modifyAt, setAt)-import Data.Bool.Tools(whether)--import Control.Monad(replicateM, forM_, forever, replicateM_, when)-import Control.Monad.Tools(doWhile_)-import Control.Arrow((***))-import Control.Concurrent(- forkIO, ThreadId, Chan, newChan, writeChan, readChan, threadWaitRead,- killThread, threadDelay)--import System.Posix.Types-import Foreign.C.Types--data Field = Field{- fDisplay :: Display,- fWindow :: Window,- fGC :: GC,- fGCBG :: GC,- fDel :: Atom,- fUndoBuf :: Pixmap,- fBG :: Pixmap,- fBuf :: Pixmap,- fWidth :: IORef Dimension,- fHeight :: IORef Dimension,- fBuffed :: IORef [IO ()],- fLayers :: IORef [[Bool -> IO ()]],- fCharacters :: IORef [IO ()],- fWait :: Chan (),- fEvent :: Chan (Maybe Event),- fClose :: Chan (),- fClosed :: IORef Bool,- fRunning :: IORef [ThreadId],- fOnclick :: IORef (Double -> Double -> IO Bool),- fEnd :: Chan ()- }--data Layer = Layer{- layerField :: Field,- layerId :: Int- }--data Character = Character{- characterField :: Field,- characterId :: Int- }--openField :: IO Field-openField = do- _ <- initThreads- dpy <- openDisplay ""- del <- internAtom dpy "WM_DELETE_WINDOW" True- let scr = defaultScreen dpy- root <- rootWindow dpy scr- (_, _, _, rWidth, rHeight, _, _) <- getGeometry dpy root- let black = blackPixel dpy scr- white = whitePixel dpy scr- depth = defaultDepth dpy scr- bufs <- replicateM 3 $ createPixmap dpy root rWidth rHeight depth- win <- createSimpleWindow dpy root 0 0 rWidth rHeight 1 black white- [gc, gcBG] <- replicateM 2 $ createGC dpy win- setForeground dpy gcBG 0xffffff- forM_ bufs $ \bf -> fillRectangle dpy bf gcBG 0 0 rWidth rHeight- setWMProtocols dpy win [del]- selectInput dpy win $ exposureMask .|. keyPressMask .|. buttonPressMask- mapWindow dpy win- [widthRef, heightRef] <- mapM newIORef [rWidth, rHeight]- buffActions <- newIORef []- layerActions <- newIORef []- characterActions <- newIORef []- wait <- newChan- event <- newChan- close <- newChan- closed <- newIORef False- running <- newIORef []- onclickRef <- newIORef $ const $ const $ return True- endRef <- newChan- writeChan wait ()- let f = Field{- fDisplay = dpy,- fWindow = win,- fGC = gc,- fGCBG = gcBG,- fDel = del,- fUndoBuf = head bufs,- fBG = bufs !! 1,- fBuf = bufs !! 2,- fWidth = widthRef,- fHeight = heightRef,- fBuffed = buffActions,- fLayers = layerActions,- fCharacters = characterActions,- fWait = wait,- fEvent = event,- fClose = close,- fClosed = closed,- fRunning = running,- fOnclick = onclickRef,- fEnd = endRef- }- _ <- forkIOX $ runLoop f- flushWindow f- return f--runLoop :: Field -> IO ()-runLoop f = allocaXEvent $ \e -> do- endc <- waitInput f- th1 <- forkIOX $ forever $ do- evN <- pending $ fDisplay f- replicateM_ (fromIntegral evN) $ do- nextEvent (fDisplay f) e- ev <- getEvent e- writeChan (fEvent f) $ Just ev- end <- readChan endc- when end $ writeChan (fEvent f) Nothing- (>> (closeDisplay (fDisplay f) >> writeChan (fEnd f) ())) $- (>> destroyWindow (fDisplay f) (fWindow f)) $ doWhile_ $ do- mev <- readChan $ fEvent f- case mev of- Just (ExposeEvent{}) -> do- (_, _, _, width, height, _, _) <-- getGeometry (fDisplay f) (fWindow f)- writeIORef (fWidth f) width- writeIORef (fHeight f) height- redrawAll f- return True- Just (KeyEvent{}) -> return True- Just ev@ButtonEvent{} -> do- pos <- convertPosRev f (ev_x ev) (ev_y ev)- readIORef (fOnclick f) >>= ($ pos) . uncurry- Just ev@ClientMessageEvent{} ->- return $ convert (head $ ev_data ev) /= fDel f- Nothing -> killThread th1 >> return False- _ -> return True--onclick :: Field -> (Double -> Double -> IO Bool) -> IO ()-onclick f act = writeIORef (fOnclick f) act--fieldColor :: Field -> Pixel -> IO ()-fieldColor f clr = do- setForeground (fDisplay f) (fGCBG f) clr- let bufs = [fUndoBuf f, fBG f, fBuf f]- width <- readIORef $ fWidth f- height <- readIORef $ fHeight f- forM_ bufs $ \bf -> fillRectangle (fDisplay f) bf (fGCBG f) 0 0 width height- redrawAll f--getConnection :: Field -> Fd-getConnection = Fd . connectionNumber . fDisplay--waitInput :: Field -> IO (Chan Bool)-waitInput f = do- c <- newChan- _ <- forkIOX $ forever $ do- threadWaitRead $ getConnection f- writeChan c False- _ <- forkIO $ do- readChan $ fClose f- writeChan c True- return c--closeField :: Field -> IO ()-closeField f = do- readIORef (fRunning f) >>= mapM_ killThread- threadDelay 100000- writeChan (fClose f) ()- writeIORef (fClosed f) True--addThread :: Field -> ThreadId -> IO ()-addThread f tid = modifyIORef (fRunning f) (tid :)--flushLayer :: Layer -> IO ()-flushLayer = flushWindow . layerField--addLayer :: Field -> IO Layer-addLayer f = do- ls <- readIORef $ fLayers f- writeIORef (fLayers f) (ls ++ [[]])- modifyIORef (fBuffed f) (++ [return ()])- return Layer{layerField = f, layerId = length ls}--addCharacter :: Field -> IO Character-addCharacter f = do- cs <- readIORef $ fCharacters f- writeIORef (fCharacters f) (cs ++ [return ()])- return Character{characterField = f, characterId = length cs}--runIfOpened :: Field -> IO a -> IO ()-runIfOpened f act = do- cl <- readIORef $ fClosed f- if cl then return () else act >> return ()--drawLineNotFlush ::- Layer -> Double -> Pixel -> Double -> Double -> Double -> Double -> IO ()-drawLineNotFlush l@Layer{layerField = f} lw_ clr x1 y1 x2 y2 = runIfOpened f $ do- drawLineBuf f lw clr fBG x1 y1 x2 y2- addLayerAction l $ whether- (drawLineBuf f lw clr fUndoBuf x1 y1 x2 y2)- (drawLineBuf f lw clr fBG x1 y1 x2 y2)- where- lw = round lw_--drawLine :: Layer -> Double -> Pixel -> Double -> Double -> Double -> Double -> IO ()-drawLine l@Layer{layerField = f} lw_ clr x1 y1 x2 y2 = runIfOpened f $ do- drawLineBuf f lw clr fBG x1 y1 x2 y2 >> redrawCharacters f- addLayerAction l $ whether- (drawLineBuf f lw clr fUndoBuf x1 y1 x2 y2)- (drawLineBuf f lw clr fBG x1 y1 x2 y2)- where- lw = round lw_--clearCharacter :: Character -> IO ()-clearCharacter c = runIfOpened (characterField c) $- setCharacter c $ return ()--drawCharacter :: Character -> Pixel -> [(Double, Double)] -> IO ()-drawCharacter c@Character{characterField = f} clr sh = do- runIfOpened (characterField c) $ setCharacter c $ do- setForeground (fDisplay f) (fGC f) clr- fillPolygonBuf (characterField c) sh--drawCharacterAndLine :: Character -> Pixel -> [(Double, Double)] -> Double ->- Double -> Double -> Double -> Double -> IO ()-drawCharacterAndLine c@Character{characterField = f} clr ps lw_ x1 y1 x2 y2 = do- runIfOpened f $ setCharacter c $ do- setForeground (fDisplay f) (fGC f) clr- fillPolygonBuf f ps >> drawLineBuf f lw clr fBuf x1 y1 x2 y2- where- lw = round lw_--undoLayer :: Layer -> IO Bool-undoLayer Layer{layerField = f, layerId = lid} = do- ls <- readIORef $ fLayers f- if null $ ls !! lid then return False else do- writeIORef (fLayers f) $ modifyAt ls lid init- redraw f- return True--clearLayer :: Layer -> IO ()-clearLayer Layer{layerField = f, layerId = lid} = do- ls <- readIORef $ fLayers f- writeIORef (fLayers f) $ setAt ls lid []- buffed <- readIORef $ fBuffed f- writeIORef (fBuffed f) $ setAt buffed lid $ return ()- redrawBuf f- redraw f--forkIOX :: IO () -> IO ThreadId-forkIOX = (initThreads >>) . forkIO------------------------------------------------------------------------------------undoN :: Int-undoN = 100--addLayerAction :: Layer -> (Bool -> IO ()) -> IO ()-addLayerAction Layer{layerField = f, layerId = lid} act = do- ls <- readIORef $ fLayers f- if length (ls !! lid) > undoN- then do head (ls !! lid) True- buffed <- readIORef $ fBuffed f- writeIORef (fBuffed f) $ - modifyAt buffed lid (>> head (ls !! lid) True)- writeIORef (fLayers f) $- modifyAt ls lid $ (++ [act]) . tail- else writeIORef (fLayers f) $ modifyAt ls lid (++ [act])--setCharacter :: Character -> IO () -> IO ()-setCharacter Character{characterField = f, characterId = cid} act = do- cs <- readIORef $ fCharacters f- writeIORef (fCharacters f) $ setAt cs cid act- redrawCharacters f- flushWindow f--fillPolygonBuf :: Field -> [(Double, Double)] -> IO ()-fillPolygonBuf f ps_ = do- ps <- convertPos f ps_- fillPolygon (fDisplay f) (fBuf f) (fGC f) (map (uncurry Point) ps)- nonconvex coordModeOrigin--drawLineBuf :: Field -> Int -> Pixel -> (Field -> Pixmap) ->- Double -> Double -> Double -> Double -> IO ()-drawLineBuf f@Field{fDisplay = dpy, fGC = gc} lw clr bf x1_ y1_ x2_ y2_ = do- setForeground (fDisplay f) (fGC f) clr- setLineAttributes (fDisplay f) (fGC f) (fromIntegral lw) lineSolid capRound joinRound- [(x1, y1), (x2, y2)] <- convertPos f [(x1_, y1_), (x2_, y2_)]- X.drawLine dpy (bf f) gc x1 y1 x2 y2--convertPos :: Field -> [(Double, Double)] -> IO [(Position, Position)]-convertPos f ps = do- (width, height) <- fieldSize f- return $ (round . (+ width / 2) *** round . (+ height / 2) . negate)- `map` ps--convertPosRev :: Field -> CInt -> CInt -> IO (Double, Double)-convertPosRev f x y = do- (width, height) <- fieldSize f- return $ (fromIntegral x - width / 2, fromIntegral (- y) + height / 2)--fieldSize :: Field -> IO (Double, Double)-fieldSize w = fmap (fromIntegral *** fromIntegral) $ winSize w--winSize :: Field -> IO (Dimension, Dimension)-winSize f = do- width <- readIORef $ fWidth f- height <- readIORef $ fHeight f- return (width, height)--redrawAll :: Field -> IO ()-redrawAll f = do- redrawBuf f- redraw f- flushWindow f--redrawBuf :: Field -> IO ()-redrawBuf f = do- winSize f >>=- uncurry (fillRectangle (fDisplay f) (fUndoBuf f) (fGCBG f) 0 0)- readIORef (fBuffed f) >>= sequence_--redraw :: Field -> IO ()-redraw = withLock $ \f -> do- (width, height) <- winSize f- copyArea (fDisplay f) (fUndoBuf f) (fBG f) (fGC f) 0 0 width height 0 0- readIORef (fLayers f) >>= mapM_ ($ False) . concat- copyArea (fDisplay f) (fBG f) (fBuf f) (fGC f) 0 0 width height 0 0- readIORef (fCharacters f) >>= sequence_--redrawCharacters :: Field -> IO ()-redrawCharacters = withLock $ \f -> do- (width, height) <- winSize f- copyArea (fDisplay f) (fBG f) (fBuf f) (fGC f) 0 0 width height 0 0- readIORef (fCharacters f) >>= sequence_--flushWindow :: Field -> IO ()-flushWindow = withLock $ \f -> do- (width, height) <- winSize f- copyArea (fDisplay f) (fBuf f) (fWindow f) (fGC f) 0 0 width height 0 0- flush $ fDisplay f--withLock :: (Field -> IO a) -> Field -> IO a-withLock act f = do- readChan $ fWait f- ret <- act f- writeChan (fWait f) ()- return ret--waitField :: Field -> IO ()-waitField = readChan . fEnd
xturtle.cabal view
@@ -2,7 +2,7 @@ cabal-version: >= 1.6 name: xturtle-version: 0.0.12+version: 0.0.13 stability: experimental author: Yoshikuni Jujo <PAF01143@nifty.ne.jp> maintainer: Yoshikuni Jujo <PAF01143@nifty.ne.jp>@@ -30,7 +30,8 @@ library Hs-source-dirs: src Exposed-modules: Graphics.X11.Turtle- Build-depends: base > 3 && < 5, yjtools >= 0.9.13, convertible, X11+ Build-depends: base > 3 && < 5, yjtools >= 0.9.13, convertible, X11,+ X11-xft <= 3.1, x11-xim, setlocale Ghc-options: -Wall- Other-modules: Graphics.X11.WindowLayers, Graphics.X11.TurtleInput,+ Other-modules: Graphics.X11.TurtleField, Graphics.X11.TurtleInput, Graphics.X11.TurtleMove, Graphics.X11.TurtleState, Graphics.X11.TurtleShape