module Graphics.X11.Turtle.XTools( -- * types Display, Window, Pixmap, XIC, Atom, Point(..), PositionXT, Dimension, Bufs, undoBuf, bgBuf, topBuf, GCs, gcForeground, gcBackground, -- ** event types XEventPtr, Event(..), -- * basic functions forkIOX, openWindow, destroyWindow, closeDisplay, windowSize, -- * draw functions flush, setForegroundXT, copyArea, fillRectangleXT, fillPolygonXT, drawLineXT, writeStringXT, drawImageXT, -- * event functions allocaXEvent, waitEvent, pending, nextEvent, getEvent, filterEvent, utf8LookupString, buttonPress, buttonRelease, xK_VoidSymbol, ) where import Text.XML.YJSVG(Color(..)) import Graphics.X11( Display, Drawable, Window, Pixmap, GC, Atom, Point(..), Position, Dimension, XEventPtr, initThreads, flush, supportsLocale, setLocaleModifiers, connectionNumber, openDisplay, closeDisplay, internAtom, createSimpleWindow, destroyWindow, mapWindow, getGeometry, createGC, createPixmap, rootWindow, defaultScreen, defaultScreenOfDisplay, defaultVisual, defaultColormap, defaultDepth, whitePixel, blackPixel, copyArea, fillRectangle, fillPolygon, drawLine, drawPoint, nonconvex, coordModeOrigin, setLineAttributes, lineSolid, capRound, joinRound, setForeground, allocNamedColor, color_pixel, setWMProtocols, selectInput, allocaXEvent, pending, nextEvent, exposureMask, keyPressMask, buttonPressMask, buttonReleaseMask, pointerMotionMask, buttonPress, buttonRelease, xK_VoidSymbol) import Graphics.X11.Xlib.Extras(Event(..), getEvent) import Graphics.X11.Xft( xftDrawCreate, xftFontOpen, withXftColorValue, withXftColorName, xftDrawString) import Graphics.X11.Xrender(XRenderColor(..)) import Graphics.X11.Xim( XIC, XNInputStyle(..), openIM, createIC, getICValue, filterEvent, utf8LookupString) import Graphics.Imlib( ImlibLoadError(..), loadImageWithErrorReturn, contextSetImage, imageGetWidth, imageGetHeight, imageGetData, createCroppedScaledImage) import Numeric(showFFloat) import Control.Monad(forM_, replicateM) import Control.Monad.Tools(unlessM) import Control.Concurrent(ThreadId, forkIO, threadWaitRead) import System.Locale.SetLocale(Category(..), setLocale) import System.Posix.Types(Fd(..)) import Data.Word(Word32) import Data.Bits((.|.), shift) import Data.IORef(newIORef, readIORef) import Data.IORef.Tools(atomicModifyIORef_) import Foreign.Ptr(Ptr) import Foreign.Storable(peek) import Foreign.Marshal.Array(advancePtr) -------------------------------------------------------------------------------- type PositionXT = Position data Bufs = Bufs{undoBuf :: Pixmap, bgBuf :: Pixmap, topBuf :: Pixmap} data GCs = GCs{gcForeground :: GC, gcBackground :: GC} -------------------------------------------------------------------------------- forkIOX :: IO () -> IO ThreadId forkIOX = (initThreads >>) . forkIO openWindow :: IO (Display, Window, Bufs, GCs, XIC, Atom, (Dimension, Dimension)) openWindow = do _ <- setLocale LC_CTYPE Nothing >>= maybe (error "setLocale") return _ <- initThreads unlessM supportsLocale $ error "Current locale is not supported." _ <- setLocaleModifiers "" dpy <- openDisplay "" del <- internAtom dpy "WM_DELETE_WINDOW" True let scr = defaultScreen dpy root <- rootWindow dpy scr (rWidth, rHeight) <- windowSize dpy root bufs@[ub, bb, tb] <- replicateM 3 $ createPixmap dpy root rWidth rHeight $ defaultDepth dpy scr win <- createSimpleWindow dpy root 0 0 rWidth rHeight 1 (blackPixel dpy scr) (whitePixel dpy scr) 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 $ \buf -> fillRectangle dpy buf gcBG 0 0 rWidth rHeight setWMProtocols dpy win [del] selectInput dpy win $ fevent .|. exposureMask .|. keyPressMask .|. buttonPressMask .|. buttonReleaseMask .|. pointerMotionMask size <- mapWindow dpy win >> windowSize dpy win return (dpy, win, Bufs ub bb tb, GCs gc gcBG, ic, del, size) windowSize :: Display -> Window -> IO (Dimension, Dimension) windowSize dpy win = do (_, _, _, width, height, _, _) <- getGeometry dpy win return (width, height) -------------------------------------------------------------------------------- setForegroundXT :: Display -> GC -> Color -> IO () setForegroundXT dpy gc (RGB r g b) = setForeground dpy gc $ shift (fromIntegral r) 16 .|. shift (fromIntegral g) 8 .|. fromIntegral b setForegroundXT dpy gc (ColorName cn) = (allocNamedColor dpy (defaultColormap dpy $ defaultScreen dpy) cn >>= setForeground dpy gc . color_pixel . fst) `catch` const (putStrLn "no such color") fillRectangleXT :: Display -> Drawable -> GC -> Position -> Position -> Dimension -> Dimension -> IO () fillRectangleXT = fillRectangle fillPolygonXT :: Display -> Drawable -> GC -> [Point] -> IO () fillPolygonXT d w gc ps = fillPolygon d w gc ps nonconvex coordModeOrigin drawLineXT :: Display -> GC -> Drawable -> Int -> Color -> Position -> Position -> Position -> Position -> IO () drawLineXT dpy gc buf lw clr x1 y1 x2 y2 = do setForegroundXT dpy gc clr setLineAttributes dpy gc (fromIntegral lw) lineSolid capRound joinRound drawLine dpy buf gc x1 y1 x2 y2 writeStringXT :: Display -> Drawable -> String -> Double -> Color -> Position -> Position -> String -> IO () writeStringXT dpy buf fname size clr x y str = do let visual = defaultVisual dpy $ defaultScreen dpy colormap = defaultColormap dpy $ defaultScreen dpy font = fname ++ "-" ++ showFFloat (Just 0) size "" xftDraw <- xftDrawCreate dpy buf visual colormap xftFont <- xftFontOpen dpy (defaultScreenOfDisplay dpy) font case clr of RGB r g b -> withXftColorValue dpy visual colormap color $ \c -> xftDrawString xftDraw c xftFont x y str where color = XRenderColor { xrendercolor_red = fromIntegral r * 0x100, xrendercolor_blue = fromIntegral g * 0x100, xrendercolor_green = fromIntegral b * 0x100, xrendercolor_alpha = 0xffff} ColorName cn -> withXftColorName dpy visual colormap cn $ \c -> xftDrawString xftDraw c xftFont x y str drawImageXT :: Display -> Drawable -> GC -> FilePath -> Position -> Position -> Dimension -> Dimension -> IO () drawImageXT dpy win gc fp x y w h = getImage fp w h >>= maybe (return ()) (drawBitmap dpy win gc x y w h) getImage :: FilePath -> Dimension -> Dimension -> IO (Maybe (Ptr Word32)) getImage fp nw nh = do (img, err) <- loadImageWithErrorReturn fp case err of ImlibLoadErrorNone -> do contextSetImage img let zero = 0 :: Position w <- fmap fromIntegral imageGetWidth :: IO Dimension h <- fmap fromIntegral imageGetHeight :: IO Dimension img' <- createCroppedScaledImage zero zero w h nw nh contextSetImage img' fmap Just imageGetData _ -> print err >> return Nothing drawBitmap :: Display -> Drawable -> GC -> Position -> Position -> Dimension -> Dimension -> Ptr Word32 -> IO () drawBitmap dpy win gc x0 y0 w_ h_ dat = do ptr <- newIORef dat forM_ [0 .. w * h - 1] $ \i -> do readIORef ptr >>= peek >>= setForeground dpy gc drawPoint dpy win gc (x0 + i `mod` w) (y0 + i `div` w) atomicModifyIORef_ ptr $ flip advancePtr 1 where [w, h] = map fromIntegral [w_, h_] -------------------------------------------------------------------------------- waitEvent :: Display -> IO () waitEvent = threadWaitRead . Fd . connectionNumber