packages feed

vacuum-opengl (empty) → 0.0

raw patch · 7 files changed

+534/−0 lines, 7 filesdep +GLUTdep +OpenGLdep +basesetup-changed

Dependencies added: GLUT, OpenGL, base, directory, network, pretty, process, stb-image, vacuum

Files

+ LICENSE view
@@ -0,0 +1,32 @@+http://creativecommons.org/licenses/publicdomain/+-------------------------------------------------++Copyright-Only Dedication (based on United States law) or +Public Domain Certification++The person or persons who have associated work with this document (the +"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of +his knowledge, the work of authorship identified is in the public domain +of the country from which the work is published, or (b) hereby dedicates +whatever copyright the dedicators holds in the work of authorship identified +below (the "Work") to the public domain. A certifier, moreover, dedicates any +copyright interest he may have in the associated work, and for these purposes, +is described as a "dedicator" below.++A certifier has taken reasonable steps to verify the copyright status of this +work. Certifier recognizes that his good faith efforts may not shield him +from liability if in fact the work certified is not in the public domain.++Dedicator makes this dedication for the benefit of the public at large and +to the detriment of the Dedicator's heirs and successors. Dedicator intends +this dedication to be an overt act of relinquishment in perpetuity of all +present and future rights under copyright law, whether vested or contingent, +in the Work. Dedicator understands that such relinquishment of all rights +includes the relinquishment of all rights to enforce (by lawsuit or otherwise) +those copyrights in the Work.++Dedicator recognizes that, once placed in the public domain, the Work may +be freely reproduced, distributed, transmitted, used, modified, built upon, +or otherwise exploited by anyone for any purpose, commercial or non-commercial, +and in any way, including by methods that have not yet been invented or +conceived.
+ ServerMain.hs view
@@ -0,0 +1,7 @@++module Main where++import System.Vacuum.OpenGL.Server++main :: IO ()+main = serverMain
+ Setup.lhs view
@@ -0,0 +1,5 @@+#! /usr/bin/env runhaskell+> +> import Distribution.Simple+> main = defaultMain+>
+ System/Vacuum/OpenGL.hs view
@@ -0,0 +1,7 @@++module System.Vacuum.OpenGL+  ( module System.Vacuum.OpenGL.Client+  )+  where+  +import System.Vacuum.OpenGL.Client
+ System/Vacuum/OpenGL/Client.hs view
@@ -0,0 +1,125 @@++-- | Since GLUT+GHCi is problematic at best, after some very frustating+-- experiments I decided on a client-server architecture.+--+-- Usage example:+--+-- > $ vacuum-opengl-server &+-- > vaacum-opengl server started - press ESC to exit. +-- > listening on port 55961+-- > +-- > $ ghci+-- > Prelude> :m + System.Vacuum.OpenGL +-- > Prelude System.Vacuum.OpenGL> view [1..5]+-- > Prelude System.Vacuum.OpenGL> view $ zip "foobar" [1..6]+-- > Prelude System.Vacuum.OpenGL> :m + Data.Set+-- > Prelude System.Vacuum.OpenGL Data.Set> view $ fromList [1..10]+--+-- Note: +-- +--  * we need the graphviz executable (called \"dot\") being in the path.+--+--  * we need the server running+--+--  * the image generated by graphviz may be bigger than the maximum texture +--    size your video card supports (eg. 2048). In this case you will see+--    a white rectangle. TODO: handle this better.+--++module System.Vacuum.OpenGL.Client +  ( view+  , changePort+  , changeHost+  ) +  where++--------------------------------------------------------------------------------++import Control.Monad+import Control.Concurrent.MVar++--import Control.Exception++import Data.Char++import GHC.Vacuum+import Text.PrettyPrint.HughesPJ++import System.IO.Unsafe++import Network++--------------------------------------------------------------------------------++defaultPort :: PortID +defaultPort = PortNumber VACUUM_OPENGL_DEFAULTPORT++defaultHost :: HostName+defaultHost = "127.0.0.1"++--------------------------------------------------------------------------------++thePort :: MVar PortID+thePort = unsafePerformIO $ newMVar defaultPort++theHost :: MVar HostName+theHost = unsafePerformIO $ newMVar defaultHost++--------------------------------------------------------------------------------++send :: String -> IO ()+send text = withSocketsDo $ do+  port <- readMVar thePort+  host <- readMVar theHost+  sendTo host port text++changePort :: Int -> IO ()+changePort n = do+  swapMVar thePort (PortNumber $ fromIntegral n)+  return ()++changeHost :: String -> IO ()+changeHost s = do+  swapMVar theHost s+  return ()+  +view :: a -> IO ()+view x = do+  --let dot = (ppDot . nameGraph . vacuum) x+  let dot = (ppDot . showHNodes showHN . vacuum) x+  send (show dot)+  +--------------------------------------------------------------------------------++-- this function is based on the corresponding one in Don Stewart's vacuum-cairo library  +showHN :: ShowHNode+showHN = ShowHNode { showHNode = renderNode , externHNode  = \_ -> "..." }+  where+    renderNode i n = node n ++ "|" ++ show i -- unique id+    showLit n = show (head $ nodeLits n)+    node n = case nodeName n of+      ":"  -> "(:)"++      -- atomic stuff is special+      k | k `elem` [ "S#", "I#", "W#"+                   , "I8#", "I16#", "I32#", "I64#"+                   , "W8#", "W16#", "W32#", "W64#"+                   ] -> k ++ "(" ++ showLit n ++ ")"++      "D#" -> "Double"+      "F#" -> "Float"++      -- chars+      "C#" -> "C#(" ++ (show . chr . fromIntegral . head . nodeLits) n ++ ")"++      -- bytestrings+      "PS"    -> "ByteString[" ++ show (nodeLits n !! 1) ++ "," ++ show (nodeLits n !! 2) ++ "]"+      "Chunk" -> "Chunk["      ++ show (nodeLits n !! 1) ++ "," ++ show (nodeLits n !! 2) ++ "]"++      -- otherwise just the constructor and local fields+      c | z > 0     -> c ++ show (take (fromIntegral z) $ nodeLits n)+        | otherwise -> c+        where z = itabLits (nodeInfo n)+ +--------------------------------------------------------------------------------+
+ System/Vacuum/OpenGL/Server.hs view
@@ -0,0 +1,302 @@++-- | The vacuum-opengl server. This compiles to the executable+-- \"vacuum-opengl-server\", which you need to start to be able+-- to use the +--++module System.Vacuum.OpenGL.Server where++--------------------------------------------------------------------------------++import Control.Monad+import Control.Concurrent+import Control.Concurrent.MVar++import Control.Exception++import Data.Char++import Graphics.Rendering.OpenGL+import Graphics.UI.GLUT++import Codec.Image.STB++import System.Cmd+import System.Directory+import System.Environment+import System.Exit++import System.IO+import System.IO.Unsafe++import Network++import Foreign++--------------------------------------------------------------------------------++defaultPort :: PortID +defaultPort = PortNumber VACUUM_OPENGL_DEFAULTPORT++--------------------------------------------------------------------------------++theTexture :: MVar (Maybe (TextureObject,(Int,Int)))+theTexture = unsafePerformIO $ newMVar Nothing++theImage :: MVar (Maybe Image)+theImage = unsafePerformIO $ newMVar Nothing++--------------------------------------------------------------------------------++showPort :: PortID -> String+showPort p = case p of+  PortNumber n -> show n+--  Service s    -> s+--  UnixSocket s -> s     -- UnixSocket does not exists on Windows?+  _ -> "unknown port type"+    +startServer :: PortID -> IO ()+startServer port = withSocketsDo $ do+  putStrLn "vacuum-opengl-server started - press ESC to exit."+  putStrLn $ "listening on port " ++ showPort port+  listen port++listen port = do +  socket <- listenOn port+  sequence_ $ repeat $ acceptConnectionAndFork socket++showConn :: (Handle, HostName, PortNumber) -> String+showConn (handle, hostname, portnumber) = hostname ++ ":" ++ show portnumber++acceptConnectionAndFork :: Socket -> IO ()+acceptConnectionAndFork socket = do+  conn <- accept socket+  forkIO (server conn)+  return ()+  +server conn@(handle, hostname, portnumber) = do+  text <- hGetContents handle+  length text `seq` view text+  hClose handle+    +--------------------------------------------------------------------------------++graphvizPath :: MVar FilePath+graphvizPath = unsafePerformIO $ do +  findExecutable "dot" >>= \m -> case m of+    Nothing   -> error "graphviz executable \"dot\" not found"+    Just path -> newMVar path++runGraphviz :: [String] -> IO ()+runGraphviz params = do+  path <- readMVar graphvizPath+  exitcode <- rawSystem path params+  when (exitcode /= ExitSuccess) $ +    error "error running graphviz"+  return ()+      +convertToPNG :: String -> IO Image+convertToPNG input = do+  tmp <- getTemporaryDirectory+  let dot = tmp ++ "vacuum-opengl-temp.dot"+      png = tmp ++ "vacuum-opengl-temp.png"+  writeFile dot input+ +--  print dot+--  print png+  +  runGraphviz [ "-Tpng" , "-o"++png , dot ]++  img <- loadImage png >>= \m -> case m of+    Left err -> error err+    Right img -> return img      +    +  removeFile dot+  removeFile png  ++  return img+  +view :: String -> IO ()+view dot = do+  img <- convertToPNG dot+  swapMVar theImage (Just img)+  win <- readMVar theWindow+  postRedisplay (Just win)+  return ()+ +--------------------------------------------------------------------------------++setTexture :: IO ()+setTexture = do++  readMVar theImage >>= \m -> case m of +  +    Nothing -> return ()+    Just img -> do++      swapMVar theImage Nothing++      takeMVar theTexture >>= \m -> case m of+        Just (old,_) -> deleteObjectNames [old]+        Nothing -> return ()+    +      b <- readMVar npot+      let myWithImage = if b then withImage else withExtendedImage  +      +      tex <- myWithImage img $ \p (x,y) c -> do+    +        let (pf,pif) = case c of+          { 1 -> ( Luminance,      Luminance8       ) +          ; 2 -> ( LuminanceAlpha, Luminance8Alpha8 )+          ; 3 -> ( RGB,            RGB8             )+          ; 4 -> ( RGBA,           RGBA8            )+          }+        let size = TextureSize2D (fromIntegral x) (fromIntegral y) +            pdata = PixelData pf UnsignedByte p              +    +        [tex] <- genObjectNames 1 +    +        textureBinding Texture2D $= Just tex +        texImage2D Nothing NoProxy 0 pif size 0 pdata+        textureFilter Texture2D $= ((Linear',Nothing),Linear')+        return tex+      +      putMVar theTexture $ Just (tex,resolution img)+  ++--------------------------------------------------------------------------------++vt :: Double -> Double -> IO ()+vt x y = vertex (Vertex2 x y)++tc :: Double -> Double -> IO ()+tc x y = texCoord (TexCoord2 x y)++-- display callback  +display :: IO ()+display = do+  win <- readMVar theWindow +  currentWindow $= Just win+  +  clearColor $= Color4 0.90 0.90 0.90 1+  clear [ColorBuffer] ++  setTexture+  +  readMVar theTexture >>= \m -> case m of +    Nothing -> return ()+    Just (tex,(xsize,ysize)) -> do+        +      size@(Size xres yres) <- get windowSize+      viewport $= ( Position 0 0 , size )+      let winaspect = fromIntegral xres  / fromIntegral yres  :: Double+          picaspect = fromIntegral xsize / fromIntegral ysize :: Double+      matrixMode $= Projection >> loadIdentity+      ortho (-1) 1 (-1) 1 (-1) 1+              +      texture Texture2D $= Enabled+      textureBinding Texture2D $= Just tex+      b <- readMVar npot+      let (x,y) = if picaspect > winaspect then (1,winaspect/picaspect) else (picaspect/winaspect,1)+          myExtendDimension = if b then id else nextPowerOfTwo+          u = fromIntegral xsize / fromIntegral (myExtendDimension xsize) +          v = fromIntegral ysize / fromIntegral (myExtendDimension ysize) +      renderPrimitive Quads $ do +        tc 0 0 ; vt (-x) ( y)+        tc u 0 ; vt ( x) ( y)+        tc u v ; vt ( x) (-y)+        tc 0 v ; vt (-x) (-y)+         +  swapBuffers+  +--------------------------------------------------------------------------------+  +log2 :: Int -> Int+log2 n = case n of+  0 -> -1+  _ -> 1 + log2 (shiftR n 1) + +nextPowerOfTwo :: Int -> Int      +nextPowerOfTwo n = 2 ^ ( 1 + log2 (n-1) )+  +-- extend the image to have power-of-two sizes+withExtendedImage :: Image -> (Ptr Word8 -> (Int,Int) -> Int -> IO a) -> IO a+withExtendedImage img action = withImage img $ \p (oldx,oldy) c -> do+  let (newx,newy) = (nextPowerOfTwo oldx, nextPowerOfTwo oldy)+  +  allocaArray (newx*newy*c) $ \q -> do+    forM_ [0..oldy-1] $ \i -> copyArray +      (q `advancePtr` (i*c*newx))  -- destination +      (p `advancePtr` (i*c*oldx))  -- source+      (oldx*c)                     -- number of bytes+  +    action q (newx,newy) c+    +--------------------------------------------------------------------------------+  +-- reshape callback+reshape _ = postRedisplay Nothing+  +-- keyboard callback+keyboard key keyState mod pos = +  case key of +    Char '\ESC' -> exitWith ExitSuccess+    _ -> return ()    ++--------------------------------------------------------------------------------++theWindow :: MVar Window+theWindow = unsafePerformIO newEmptyMVar++npot :: MVar Bool+npot = unsafePerformIO newEmptyMVar++idle :: IO ()+idle = do  +  threadDelay 10000++ourInitialWinSize :: Size+ourInitialWinSize = Size 640 400++initGLUT :: IO ()+initGLUT = do+    +  initialDisplayMode $= [ RGBAMode, DoubleBuffered ]+  initialWindowSize $= ourInitialWinSize++  prog <- getProgName+  initialize prog [] +        +  win <- createWindow "vacuum-opengl-server"++  exts <- get glExtensions +  putMVar npot ("GL_ARB_texture_non_power_of_two" `elem` exts)++  displayCallback       $= display+  idleCallback          $= Just idle+  reshapeCallback       $= Just reshape+  keyboardMouseCallback $= Just keyboard++  drawBuffer $= BackBuffers+  postRedisplay (Just win)++  putMVar theWindow win+  +  mainLoop+    +--------------------------------------------------------------------------------+    +serverMain :: IO ()+serverMain = do+  +  args <- getArgs+  let port = case args of+        []    -> defaultPort+        (x:_) -> PortNumber $ fromIntegral $ (read x :: Int)++  forkOS (startServer port)+  +  initGLUT +    +--------------------------------------------------------------------------------+   
+ vacuum-opengl.cabal view
@@ -0,0 +1,56 @@+Name:                vacuum-opengl+Version:             0.0+Synopsis:            Visualize live Haskell data structures using vacuum, graphviz and OpenGL.+Description:         Visualize live Haskell data structures using vacuum, graphviz and OpenGL.+                     Intended as an easier-to-build alternative (no large dependency chain)+                     to vacuum-cairo. Because of severe problems with GHCi+GLUT, it is +                     implemented using a client-server architecture.+License:             PublicDomain+License-file:        LICENSE+Author:              Balazs Komuves+Maintainer:          bkomuves (plus) hackage (at) gmail (dot) com+Homepage:            http://code.haskell.org/~bkomuves/+Stability:           Experimental+Category:            Development+Tested-With:         GHC == 6.10.1 +Cabal-Version:       >= 1.2+Build-Type:          Simple++Flag base4+  Description: Base v4 ++Executable vacuum-opengl-server+  if flag(base4)+    Build-Depends:        base >= 4 && < 5, directory, process, network+    cpp-options:          -DBASE_MAJOR_VERSION=4+  else+    Build-Depends:        base >= 3 && < 4, directory, process, network+    cpp-options:          -DBASE_MAJOR_VERSION=3++  Build-Depends:       stb-image >= 0.1.3, OpenGL >= 2.2, GLUT >= 2.1+    +  Main-is:             ServerMain.hs+  Other-Modules:       System.Vacuum.OpenGL.Server+  +  Hs-Source-Dirs:      .+  Extensions:          CPP+  cpp-options:         -DVACUUM_OPENGL_DEFAULTPORT=55961+  ghc-options:         -Wall -threaded++Library+  if flag(base4)+    Build-Depends:        base >= 4 && < 5, pretty, network+    cpp-options:          -DBASE_MAJOR_VERSION=4+  else+    Build-Depends:        base >= 3 && < 4, pretty, network+    cpp-options:          -DBASE_MAJOR_VERSION=3++  Build-Depends:       vacuum >= 0.0.90+    +  Exposed-Modules:     System.Vacuum.OpenGL.Client,+                       System.Vacuum.OpenGL+  +  Hs-Source-Dirs:      .+  Extensions:          CPP+  cpp-options:         -DVACUUM_OPENGL_DEFAULTPORT=55961+  ghc-options:         -Wall