I create simple haskell-opengl project. It is build successful using commands:
cabal configure
cabal build
When I use stack:
stack build
I get error:
GLUT-2.7.0.1: configure
Progress
Configuring GLUT-2.7.0.1...
Setup.hs: Missing dependency on a foreign library:
* Missing C library: glut32
This problem can usually be solved by installing the system package that
provides this library (you may need the "-dev" version). If the library is
already installed but in a non-standard location then you can use the flags
--extra-include-dirs= and --extra-lib-dirs= to specify where it is.
How to make use stack glut.dll?
module Main where
import Graphics.UI.GLUT
import Foreign.Marshal.Array
import Foreign.Ptr
import Foreign.Storable()
import Foreign.C.Types()
import qualified Data.ByteString as BS
import Control.Monad
data State = State
{
vertexBuffer :: BufferObject,
gpuProgram :: Program
}
triangleVertexes :: [GLfloat]
triangleVertexes = [
0.0, 0.5, 0.0, 1.0,
0.5, -0.366, 0.0, 1.0,
-0.5, -0.366, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0
]
main :: IO ()
main = do
(progName, _) <- getArgsAndInitialize
initialDisplayMode $= [ DoubleBuffered, RGBAMode, WithAlphaComponent, WithDepthBuffer ]
_ <- createWindow progName
state <- initializeState
displayCallback $= display state
reshapeCallback $= Just (reshape state)
mainLoop
fragmentShaderFilePath :: FilePath
fragmentShaderFilePath = "shader.frag"
vertexShaderFilePath :: FilePath
vertexShaderFilePath = "shader.vert"
createVertexBuffer :: [GLfloat] -> IO BufferObject
createVertexBuffer vertexes = do
bufferObject <- genObjectName
bindBuffer ArrayBuffer $= Just bufferObject
withArrayLen vertexes $ \count arr ->
bufferData ArrayBuffer $= (fromIntegral count * 4, arr, StaticDraw)
enableAttribLocations [0, 1]
setAttribPointers
return bufferObject
vertexNumComponents :: NumComponents
vertexNumComponents = 4
colorNumComponents :: NumComponents
colorNumComponents = 4
initializeState :: IO State
initializeState = do
bufferObject <- createVertexBuffer triangleVertexes
program <- initGPUProgram
return State
{
vertexBuffer = bufferObject,
gpuProgram = program
}
loadShader :: ShaderType -> FilePath -> IO Shader
loadShader t path = do
shader <- createShader t
source <- BS.readFile path
shaderSourceBS shader $= source
compileShader shader
status <- get (compileStatus shader)
unless status $ putStrLn . (("message" ++ " log: ") ++) =<< get (shaderInfoLog shader)
return shader
initGPUProgram :: IO Program
initGPUProgram = do
vertexShader <- loadShader VertexShader vertexShaderFilePath
fragmentShader <- loadShader FragmentShader fragmentShaderFilePath
let shaders = [vertexShader, fragmentShader]
program <- createProgram
mapM_ (attachShader program) shaders
linkProgram program
mapM_ (detachShader program) shaders
return program
display :: State -> DisplayCallback
display state = do
clearColor $= Color4 1.0 0.0 1.0 1.0
clear [ ColorBuffer ]
bindBuffer ArrayBuffer $= Just (vertexBuffer state)
enableAttribLocations [0, 1]
setAttribPointers
currentProgram $= Just (gpuProgram state)
drawArrays Triangles 0 3
disableAttribLocations [0, 1]
swapBuffers
checkError "display"
setCapabilityForAttribLocations :: Capability -> [GLuint] -> IO ()
setCapabilityForAttribLocations capability =
mapM_ (\location -> vertexAttribArray (AttribLocation location) $= capability)
disableAttribLocations :: [GLuint] -> IO ()
disableAttribLocations = setCapabilityForAttribLocations Disabled
enableAttribLocations :: [GLuint] -> IO ()
enableAttribLocations = setCapabilityForAttribLocations Enabled
setAttribPointers :: IO ()
setAttribPointers = do
vertexAttribPointer (AttribLocation 0) $= (ToFloat, VertexArrayDescriptor vertexNumComponents Float 0 nullPtr)
vertexAttribPointer (AttribLocation 1) $= (ToFloat, VertexArrayDescriptor colorNumComponents Float 0 (plusPtr nullPtr 48))
reshape :: State -> ReshapeCallback
reshape _ size =
viewport $= (Position 0 0, size)
checkError :: String -> IO ()
checkError functionName = get errors >>= mapM_ reportError
where reportError e = putStrLn (showError e ++ " detected in " ++ functionName)
showError (Error category message) = "GL error " ++ show category ++ " (" ++ message ++ ")"
-- fragment shader
#version 330
smooth in vec4 theColor;
out vec4 outputColor;
void main()
{
outputColor = theColor;
}
-- vertex shader
#version 330
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
smooth out vec4 theColor;
void main()
{
gl_Position = position + vec4(0.5, 0.5, 0.0, 1.0);
theColor = color;
}
Related
I know that the Brick and the VTY hackage do not support escape sequences. VTY only supports 240 colors.
Is there any workaround to use true RGB colors and not mess up the layout?
This is an example I made, but I can't get the border right:
module BrickTest where
import Brick (simpleMain, Widget, str)
import Brick.Widgets.Border (border)
import Text.Printf (printf)
main :: IO ()
main = simpleMain $ colorWidget (255, 0, 0)
type RGB = (Int, Int, Int)
colorWidget :: RGB -> Widget ()
colorWidget (r, g, b) = border $ str (prefix ++ "a" ++ postfix)
where
prefix = printf "\ESC[38;2;%d;%d;%dm" r g b
postfix = "\ESC[0m"
output:
┌──────────────────┐
│a│
└──────────────────┘
I found a workaround. I managed to implement a function zeroWidthStr that can print any string, and Brick handles it as if it has width 0. But I can't really explain how this is working internally, and it might have some other side effects.
module BrickTest where
import Brick (Widget, raw, simpleMain, str,
(<+>))
import Brick.Widgets.Border (border)
import Data.List (intercalate)
import Data.Text.Lazy (pack)
import Graphics.Vty (defAttr)
import Graphics.Vty.Image.Internal (Image (HorizText))
import Text.Printf (printf)
main :: IO ()
main = simpleMain $ colorWidget (255, 0, 0)
type RGB = (Int, Int, Int)
colorWidget :: RGB -> Widget ()
colorWidget (r, g, b) = border $ prefix <+> str "a" <+> postfix
where
prefix = zeroWidthStr $ printf "\ESC[38;2;%d;%d;%dm" r g b
postfix = zeroWidthStr $ "\ESC[0m"
zeroWidthStr :: String -> Widget ()
-- | workaround to print any string in terminal, and hackage Brick (vty) handles it as if it has width 0
zeroWidthStr str = raw image
where
image = HorizText defAttr (pack modStr) 0 0
modStr = str ++ repeatN "\ESC\ESCa" (length str)
repeatN :: String -> Int -> String
repeatN str n = intercalate "" $ take n $ repeat str
output:
I have done numerous graphics with Haskell OpenGL. They are in my repo here: opengl-examples (the gallery is not exhaustive). However I have a problem: when I use materialShininess nothing happens. It there something to enable in order to have the shininess ?
Here is an example of one of my prog. It it not complete but I hope it's enough to identify the issue.
module CompoundFiveTetrahedra2
where
import CompoundFiveTetrahedra.Data
import Control.Monad (when)
import qualified Data.ByteString as B
import Data.IORef
import Graphics.Rendering.OpenGL.Capture (capturePPM)
import Graphics.Rendering.OpenGL.GL
import Graphics.UI.GLUT
import Text.Printf
import Utils.ConvertPPM
import Utils.OpenGL (negateNormal)
import Utils.Prism
blue,red,green,yellow,purple,white,black :: Color4 GLfloat
blue = Color4 0 0 1 1
red = Color4 1 0 0 1
green = Color4 0 1 0 1
yellow = Color4 1 1 0 1
white = Color4 1 1 1 1
black = Color4 0 0 0 1
purple = Color4 0.5 0 0.5 1
display :: IORef GLfloat -> IORef GLfloat -> IORef GLfloat -> IORef GLdouble
-> IORef GLint -> IORef GLfloat -> DisplayCallback
display rot1 rot2 rot3 zoom capture angle = do
clear [ColorBuffer, DepthBuffer]
r1 <- get rot1
r2 <- get rot2
r3 <- get rot3
z <- get zoom
a <- get angle
i <- get capture
loadIdentity
(_, size) <- get viewport
resize z size
rotate a $ Vector3 1 1 1
rotate r1 $ Vector3 1 0 0
rotate r2 $ Vector3 0 1 0
rotate r3 $ Vector3 0 0 1
mapM_ (drawEdge blue) (edges!!0)
mapM_ (drawEdge red) (edges!!1)
mapM_ (drawEdge green) (edges!!2)
mapM_ (drawEdge yellow) (edges!!3)
mapM_ (drawEdge purple) (edges!!4)
mapM_ (drawVertex blue) vertices1
mapM_ (drawVertex red) vertices2
mapM_ (drawVertex green) vertices3
mapM_ (drawVertex yellow) vertices4
mapM_ (drawVertex purple) vertices5
when (i > 0) $ do
let ppm = printf "tetrahedra%04d.ppm" i
png = printf "tetrahedra%04d.png" i
(>>=) capturePPM (B.writeFile ppm)
convert ppm png True
capture $~! (+1)
swapBuffers
drawVertex :: Color4 GLfloat -> Vertex3 GLfloat -> IO ()
drawVertex col v =
preservingMatrix $ do
translate $ toVector v
materialDiffuse Front $= col
renderObject Solid $ Sphere' 0.03 30 30
where
toVector (Vertex3 x y z) = Vector3 x y z
drawEdge :: Color4 GLfloat -> (Vertex3 GLfloat, Vertex3 GLfloat) -> IO ()
drawEdge col (v1,v2) = do
let cylinder = prism v1 v2 30 0.03
renderPrimitive Quads $ do
materialDiffuse Front $= col
mapM_ drawQuad cylinder
where
drawQuad ((w1,w2,w3,w4),n) = do
normal $ negateNormal n
vertex w1
vertex w2
vertex w3
vertex w4
resize :: Double -> Size -> IO ()
resize zoom s#(Size w h) = do
viewport $= (Position 0 0, s)
matrixMode $= Projection
loadIdentity
perspective 45.0 (w'/h') 1.0 100.0
lookAt (Vertex3 0 0 (-3 + zoom)) (Vertex3 0 0 0) (Vector3 0 1 0)
matrixMode $= Modelview 0
where
w' = realToFrac w
h' = realToFrac h
keyboard :: IORef GLfloat -> IORef GLfloat -> IORef GLfloat -> IORef GLint
-> KeyboardCallback
keyboard rot1 rot2 rot3 capture c _ =
case c of
'r' -> rot1 $~! subtract 1
't' -> rot1 $~! (+1)
'f' -> rot2 $~! subtract 1
'g' -> rot2 $~! (+1)
'v' -> rot3 $~! subtract 1
'b' -> rot3 $~! (+1)
'c' -> capture $~! (+1)
'q' -> leaveMainLoop
_ -> return ()
mouse :: IORef GLdouble -> MouseCallback
mouse zoom button keyState _ =
case (button, keyState) of
(LeftButton, Down) -> zoom $~! (+0.1)
(RightButton, Down) -> zoom $~! subtract 0.1
_ -> return ()
idle :: IORef GLfloat -> IdleCallback
idle angle = do
angle $~! (+ 2)
postRedisplay Nothing
main :: IO ()
main = do
_ <- getArgsAndInitialize
_ <- createWindow "Five tetrahedra"
initialDisplayMode $= [RGBAMode, DoubleBuffered, WithDepthBuffer]
clearColor $= black
materialAmbient Front $= black
materialShininess Front $= 80 -- THIS DOES NOT WORK
lighting $= Enabled
light (Light 0) $= Enabled
position (Light 0) $= Vertex4 0 0 (-100) 1
ambient (Light 0) $= white
diffuse (Light 0) $= white
specular (Light 0) $= white
depthFunc $= Just Lequal
depthMask $= Enabled
shadeModel $= Smooth
rot1 <- newIORef 0.0
rot2 <- newIORef 0.0
rot3 <- newIORef 0.0
zoom <- newIORef 0.0
capture <- newIORef 0
angle <- newIORef 0.0
displayCallback $= display rot1 rot2 rot3 zoom capture angle
reshapeCallback $= Just (resize 0)
keyboardCallback $= Just (keyboard rot1 rot2 rot3 capture)
mouseCallback $= Just (mouse zoom)
idleCallback $= Just (idle angle)
mainLoop
Do I miss something to enable the shininess ?
EDIT
Here is an example with the R package rgl, which is also a wrapper to OpenGL. Look at the white part on the spheres. I cannot achieve that with Haskell.
Update: Try shininess of 1.0 to see the difference more clearly at low resolutions.
The shininess parameter affects the sharpness of specular lighting, so you need to turn this type of lighting on for your materials by giving them a specular color. (By default, the specular color is black, so the effect of the shininess parameter will not be visible.) You'll also want to reduce the shininess value for this scene, because it's too high to be very visible.
Try:
materialSpecular Front $= white
materialShininess Front $= 1.0
and you'll start to see white highlights, particularly along the curved edges of your shape. The flat faces will also reflect some white light, but only when they are nearly perpendicular to a line that's mid-angle between the viewer and the light source -- it's a little complicated.
Note that the specular color of most materials is taken to be some "multiple" of white (i.e., somewhere between black for a perfectly dull material to white for the shiniest materials in the scene). The only materials with tinted specular color would be colored metals, like gold or bronze.
Some additional notes:
You're using old-style OpenGL 2.1 shading, not "modern OpenGL", so you don't have to worry so much about the "shaders" that #user2297560 is talking about. OpenGL 2.1 comes with built-in shaders to do basic shading; with modern OpenGL, you have to build everything from scratch.
As #luqui mentioned, if you're looking for materials that actually reflect other parts of the scenes, this kind of shininess won't help you.
Here is the difference. Your original code on the left, the settings above on the right, on your "compoundfivetetrahedra" example. It'll look better if you increase the size of the window.
Note that it works better on curved surfaces. Here's your cylinder example, using:
materialShininess Front $= 5
materialSpecular Front $= white
You can see the shininess on the closer sphere.
I'm trying to get a basic "draw a triangle on the screen" example working, but in Haskell with the gl and GLFW-b packages.
My code is here:
-- https://learnopengl.com/#!Getting-started/Hello-Triangle
-- stuff from base
import Control.Monad (when, mapM)
import Foreign -- includes Ptr and Marshal, among other things.
-- we qualify these names so we can tell what's from GLFW
import qualified Graphics.UI.GLFW as GLFW
-- gl funcs all already have "gl" in their name
import Graphics.GL
width = 800 :: Int
height = 600 :: Int
vertexShaderSource = [
"#version 330 core\n",
"layout (location = 0) in vec3 position;\n",
"void main()\n",
"{\n",
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n",
"}\n"]
fragmentShaderSource = [
"#version 330 core\n",
"out vec4 color;\n",
"void main()\n",
"{\n",
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n",
"}\n"]
getSrcPointer :: [String] -> IO (Ptr (Ptr GLchar), Ptr GLint)
getSrcPointer sourceLines = do
let glcharLines = map (map (fromIntegral.fromEnum)) sourceLines
let linesLengths = map (fromIntegral.length) glcharLines
linesPtrs <- mapM newArray glcharLines
linesPtrsPtr <- newArray linesPtrs
lengthsPtr <- newArray linesLengths
return (linesPtrsPtr,lengthsPtr)
-- type KeyCallback = Window -> Key -> Int -> KeyState -> ModifierKeys -> IO ()
callback :: GLFW.KeyCallback
callback window key scanCode keyState modKeys = do
putStrLn (show key)
when (key == GLFW.Key'Escape && keyState == GLFW.KeyState'Pressed)
(GLFW.setWindowShouldClose window True)
main :: IO ()
main = do
-- init GLFW and set the appropriate options
_ <- GLFW.init
GLFW.windowHint (GLFW.WindowHint'ContextVersionMajor 3)
GLFW.windowHint (GLFW.WindowHint'ContextVersionMinor 3)
GLFW.windowHint (GLFW.WindowHint'OpenGLProfile GLFW.OpenGLProfile'Core)
GLFW.windowHint (GLFW.WindowHint'Resizable False)
-- create our window
maybeWindow <- GLFW.createWindow width height "Lesson 02" Nothing Nothing
case maybeWindow of
Nothing -> do
-- somehow we failed. Nothing to do but report that and quit.
putStrLn "Failed to create a GLFW window!"
GLFW.terminate
Just window -> do
-- set our context and callback
GLFW.makeContextCurrent (Just window)
GLFW.setKeyCallback window (Just callback)
-- define the viewport dimensions
(frameWidth,frameHeight) <- GLFW.getFramebufferSize window
glViewport 0 0 (fromIntegral frameWidth) (fromIntegral frameHeight)
-- build and compile our shader program.
successP <- malloc
-- vertex shader
vertexShader <- glCreateShader GL_VERTEX_SHADER
(linesPtrsPtr,lengthsPtr) <- getSrcPointer vertexShaderSource
glShaderSource vertexShader 1 linesPtrsPtr lengthsPtr
glCompileShader vertexShader
-- check for compilation errors
glGetShaderiv vertexShader GL_COMPILE_STATUS successP
success <- peek successP
when (success == 0) $ do
putStrLn "Vertex Shader Compile Error:"
let infoLength = 512
resultP <- malloc
infoLog <- mallocArray (fromIntegral infoLength)
glGetShaderInfoLog vertexShader (fromIntegral infoLength) resultP infoLog
result <- fromIntegral <$> peek resultP
logBytes <- peekArray result infoLog
putStrLn (map (toEnum.fromEnum) logBytes)
-- fragment shader
fragmentShader <- glCreateShader GL_FRAGMENT_SHADER
(linesPtrsPtr,lengthsPtr) <- getSrcPointer fragmentShaderSource
glShaderSource fragmentShader 1 linesPtrsPtr lengthsPtr
glCompileShader fragmentShader
-- check for compilation errors
glGetShaderiv fragmentShader GL_COMPILE_STATUS successP
success <- peek successP
when (success == 0) $ do
putStrLn "Fragment Shader Compile Error:"
let infoLength = 512
resultP <- malloc
infoLog <- mallocArray (fromIntegral infoLength)
glGetShaderInfoLog fragmentShader (fromIntegral infoLength) resultP infoLog
result <- fromIntegral <$> peek resultP
logBytes <- peekArray result infoLog
putStrLn (map (toEnum.fromEnum) logBytes)
-- link up the shaders
shaderProgram <- glCreateProgram
glAttachShader shaderProgram vertexShader
glAttachShader shaderProgram fragmentShader
glLinkProgram shaderProgram
-- check for linking errors
glGetProgramiv shaderProgram GL_LINK_STATUS successP
success <- peek successP
when (success == 0) $ do
putStrLn "Program Linking Error:"
let infoLength = 512
resultP <- malloc
infoLog <- mallocArray (fromIntegral infoLength)
glGetProgramInfoLog shaderProgram (fromIntegral infoLength) resultP infoLog
result <- fromIntegral <$> peek resultP
logBytes <- peekArray result infoLog
putStrLn (map (toEnum.fromEnum) logBytes)
-- cleanup the sub-programs now that our complete shader program is ready
glDeleteShader vertexShader
glDeleteShader fragmentShader
-- setup vertex data and attribute pointers
verticesP <- newArray ([
-0.5, -0.5, 0.0, -- Left
0.5, -0.5, 0.0, -- Right
0.0, 0.5, 0.0 -- Top
] :: [GLfloat])
let verticesSize = fromIntegral $ sizeOf (0.0 :: GLfloat) * 9
vboP <- malloc :: IO (Ptr GLuint)
vaoP <- malloc :: IO (Ptr GLuint)
glGenVertexArrays 1 vaoP
glGenBuffers 1 vboP
-- Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
vao <- peek vaoP
glBindVertexArray vao
vbo <- peek vboP
glBindBuffer GL_ARRAY_BUFFER vbo
glBufferData GL_ARRAY_BUFFER verticesSize (castPtr verticesP) GL_STATIC_DRAW
let threeFloats = fromIntegral $ sizeOf (0.0::GLfloat) * 3
glVertexAttribPointer 0 3 GL_FLOAT GL_FALSE threeFloats nullPtr
glEnableVertexAttribArray 0
-- Note that this is allowed, the call to glVertexAttribPointer
-- registered VBO as the currently bound vertex buffer object so
-- afterwards we can safely unbind
glBindBuffer GL_ARRAY_BUFFER 0
-- Unbind VAO (it's always a good thing to unbind any buffer/array
-- to prevent strange bugs)
glBindVertexArray 0
-- "game loop"
let loop :: IO ()
loop = do
shouldClose <- GLFW.windowShouldClose window
if shouldClose
then return ()
else do
-- event poll
GLFW.pollEvents
-- clear the screen
glClearColor 0.2 0.3 0.3 1.0
glClear GL_COLOR_BUFFER_BIT
-- draw a triangle
glUseProgram shaderProgram
glBindVertexArray vao
glDrawArrays GL_TRIANGLES 0 3
glBindVertexArray 0
-- swap buffers and go again
GLFW.swapBuffers window
loop
loop
-- clean up the gl resources
glDeleteVertexArrays 1 vaoP
glDeleteBuffers 1 vboP
-- clean up the GLFW resources
GLFW.terminate
It compiles and runs without any reported errors, but it only shows the clear color; the triangle isn't drawn at all.
Note that when I compile and run your program, I get shader compile and program link errors. Specifically, I get errors:
Vertex Shader Compile Error:
0:1(18): error: syntax error, unexpected $end
Fragment Shader Compile Error:
0:1(18): error: syntax error, unexpected $end
and there are link errors too, of course.
On closer inspection, your glShaderSource calls are flawed. Though they are passing arrays of line pointers and line lengths for six lines per shader, the line count argument is 1. If I modify the last line of getSrcPointer to return the line count:
return (linesPtrsPtr,lengthsPtr, (fromIntegral.length) linesLengths)
and then pass this count to the glShaderSource calls in each case:
(linesPtrsPtr,lengthsPtr,count) <- getSrcPointer vertexShaderSource
glShaderSource vertexShader count linesPtrsPtr lengthsPtr
...
(linesPtrsPtr,lengthsPtr,count) <- getSrcPointer fragmentShaderSource
glShaderSource fragmentShader count linesPtrsPtr lengthsPtr
then your program works fine.
Happy triangling!
I'm trying to use taffybar as my status bar (uses Dyre framework so configuration is code). It has a widget that can show network interface statistics. In the default configuration this widget requires a String at compile time. I want it to create a widget per (non-loopback) interface dynamically instead.
This is what I have so far:
listNetworkDevices :: IO [String]
listNetworkDevices = fmap (map takeBaseName) $ getDirectoryContents "/sys/class/net/"
filterOutLoopback :: [String] -> [String]
filterOutLoopback = filter (\y -> not (elem y ["", ".", "lo"]))
netDevList :: IO [String]
netDevList = fmap filterOutLoopback listNetworkDevices
...
let nets = fmap (fmap (netMonitorNew 1)) netDevList
...
defaultTaffybar defaultTaffybarConfig {
...
endWidgets = [ tray, clock, mem, cpu] ++ nets ++ [ bat ]
}
At this point I'm down to the following compiler error:
Couldn't match expected type ‘[IO gtk-0.14.2:Graphics.UI.Gtk.Types.Widget]’
with actual type ‘IO [IO gtk-0.14.2:Graphics.UI.Gtk.Types.Widget]’
Overall this the error makes sense since I'm making an IO Widget out of every String. So IO [String] turns into IO [IO Widget].
What I don't understand is how to avoid doing this. Clearly I must have gone wrong at some point but I can't see where. I'm not even sure how to put the issue in words. I would be glad for any pointers!
Relevant material:
endWidgets docs: https://hackage.haskell.org/package/taffybar-0.4.6/docs/System-Taffybar.html#v:endWidgets
netMonitorNew docs: https://hackage.haskell.org/package/taffybar-0.4.6/docs/System-Taffybar-NetMonitor.html#v:netMonitorNew
Source on github: https://github.com/travitch/taffybar/tree/master/src/System/Taffybar
Edit: complete listing:
import System.Taffybar
import System.Taffybar.Systray
import System.Taffybar.Pager
import System.Taffybar.TaffyPager
import System.Taffybar.SimpleClock
import System.Taffybar.Battery
import System.Taffybar.NetMonitor
import System.Taffybar.Weather
import System.Taffybar.Widgets.PollingBar
import System.Taffybar.Widgets.PollingGraph
import System.Information.Memory
import System.Information.CPU
import System.Directory ( getDirectoryContents )
import System.FilePath ( takeBaseName )
import Control.Monad
memCallback = do
mi <- parseMeminfo
return [memoryUsedRatio mi]
cpuCallback = do
(userLoad, systemLoad, totalLoad) <- cpuLoad
return [totalLoad, systemLoad]
listNetworkDevices :: IO [String]
listNetworkDevices = fmap (map takeBaseName) $ getDirectoryContents "/sys/class/net/"
filterOutLoopback :: [String] -> [String]
filterOutLoopback = filter (\y -> not (elem y ["", ".", "lo"]))
netDevList :: IO [String]
netDevList = fmap filterOutLoopback listNetworkDevices
myPagerConfig = defaultPagerConfig {}
main = do
let memCfg = defaultGraphConfig { graphDataColors = [(1, 0, 0, 1)] }
cpuCfg = defaultGraphConfig { graphDataColors = [ (0, 1, 0, 1)
, (1, 0, 1, 0.5)
]
}
clock = textClockNew Nothing "<span fgcolor='orange'>%a %b %d %H:%M</span>" 1
pager = taffyPagerNew myPagerConfig
mem = pollingGraphNew memCfg 1 memCallback
cpu = pollingGraphNew cpuCfg 1 cpuCallback
bat = textBatteryNew "$percentage$% $time$" 1
tray = systrayNew
--nets = [ netMonitorNew 1 "wlp0s2" ]
nets = fmap (fmap (netMonitorNew 1)) netDevList
defaultTaffybar defaultTaffybarConfig { startWidgets = [ pager ]
, endWidgets = [ tray, clock, mem, cpu] ++ nets ++ [ bat ]
}
Edit: Error message:
.config/taffybar/taffybar.hs:
Couldn't match expected type ‘[IO
gtk-0.14.2:Graphics.UI.Gtk.Types.Widget]’
with actual type ‘IO [IO gtk-0.14.2:Graphics.UI.Gtk.Types.Widget]’
In the first argument of ‘(++)’, namely ‘nets’
In the second argument of ‘(++)’, namely ‘nets ++ [bat]’
I haven't tested this as I usually do, since I don't have taffybar installed on this machine, but I suspect the following small change should help you make progress:
main = do
let memCfg = defaultGraphConfig { graphDataColors = [(1, 0, 0, 1)] }
cpuCfg = defaultGraphConfig { graphDataColors = [ (0, 1, 0, 1)
, (1, 0, 1, 0.5)
]
}
clock = textClockNew Nothing "<span fgcolor='orange'>%a %b %d %H:%M</span>" 1
pager = taffyPagerNew myPagerConfig
mem = pollingGraphNew memCfg 1 memCallback
cpu = pollingGraphNew cpuCfg 1 cpuCallback
bat = textBatteryNew "$percentage$% $time$" 1
tray = systrayNew
-- this line is the only one that changed
nets <- fmap (fmap (netMonitorNew 1)) netDevList
defaultTaffybar defaultTaffybarConfig { startWidgets = [ pager ]
, endWidgets = [ tray, clock, mem, cpu] ++ nets ++ [ bat ]
}
There may be other problems, but this should address the one described in the question.
There are plenty of stylistic changes that could/should be made as well, of course; for example, I think I would probably write the last two lines this way instead:
nets <- netDevList
defaultTaffybar ... { ..., endWidgets = ... ++ map netMonitorNew nets ++ ... }
When I create two windows and redraw them in two different threads (one per window), it seems like all drawing goes to first created window. It constantly switches between what should be displayed in both windows. And second one remains mostly black.
The code was working well with only one window, and then I updated it - inserted currentWindow $= Just glWindow in the beginning of the functions which set callbacks and call rendering methods.
What do you think is the cause of the problems?
EDIT:
Code skeleton:
module Chart.Window where
import Graphics.UI.GLUT hiding (Window, postRedisplay, <etc>)
import qualified Graphics.UI.GLUT as GLUT
import qualified Graphics.Rendering.OpenGL as GL
data Window = Window
{ glWindow :: GLUT.Window
, viewListRef :: IORef [Line]
}
main = do
forkOS start <params1>
forkOS start <params2>
start <params> = do
win <- new <params>
run win
mainLoop
new :: Strict -> (Int, Int) -> (Int, Int) -> IO Window
new name (posx, posy) (w, h) = do
initGLUT
glWindow <- createWindow name
currentWindow $= Just glWindow
windowSize $= Size (fromIntegral w) (fromIntegral h)
windowPosition $= Position (fromIntegral posx) (fromIntegral posy)
return Window {..}
initGLUT :: IO ()
initGLUT = do
beenInit <- get initState
unless beenInit $ void getArgsAndInitialize
initialDisplayMode $= [WithDepthBuffer, DoubleBuffered, RGBAMode]
initialWindowSize $= Size 100 100
initialWindowPosition $= Position 100 100
actionOnWindowClose $= ContinueExectuion
run :: Window -> IO ()
run win#Window{..} = do
-- this will fork (with forkIO) threads
-- which will call "repaint" when chart needs to be updated
initListeners repaint
initCallbacks win
where
repaint :: [Line] -> IO ()
repaint viewList = do
writeIORef viewListRef viewList
postRedisplay win
postRedisplay Window{..} = GLUT.postRedisplay $ Just glWindow
initCallbacks win#Window{..} = do
currentWindow $= Just glWindow
GLUT.displayCallback $= display win
GLUT.idleCallback $= Just (postRedisplay win)
display Window{..} = do
currentWindow $= Just glWindow
Size w h <- get windowSize
viewList <- readIORef viewListRef
drawChart viewList otherOptions
reshapeCallback :: Window -> GLUT.ReshapeCallback
reshapeCallback win#Window{..} size#(Size w h) = do
currentWindow $= Just glWindow
GL.viewport $= (Position 0 0, size)
GL.matrixMode $= GL.Projection
GL.loadIdentity
GL.ortho2D 0 (fromIntegral w) 0 (fromIntegral h)
GL.matrixMode $= GL.Modelview 0
GL.loadIdentity
... -- send command for listener thread to change internal state and postRedisplay
drawChart viewList otherOptions = do
...
-- chart consists of several horizontal panels. Call this for each panel:
glViewport 0 panelYPosition width winHeight
glScissor 0 panelYPosition (fromIntegral width) (fromIntegral panelHeight)
GL.clear [GL.ColorBuffer]
...
-- and then for each line=(Vertex2 v1 v2) from viewList
GL.renderPrimitive GL.Lines $ do
GL.vertex v1
GL.vertex v2
...
BTW, when I commented the line which sets reshapeCallback (and window is reshaped at the beginning) and launched charting with only one window, I got exactly the same effect as in multi-window launch. I mean, the (only) window was mostly empty as if it was secondly created.
I had a similar problem. I work with a thread that calculates the iterations of a genetic algorithm and in each iteration I call to "GL.postRedisplay (Just window)" but it didn't draw anything.
I solved my problem by calling "GL.postRedisplay (Just window)" from the idle function:
idle window = CC.threadDelay (1000*500) >> GL.postRedisplay (Just window)
Don't forget to setup your idle callback function like this:
GL.idleCallback GL.$= Just (idle window) >>
CC and GL mean:
import qualified Control.Concurrent as CC
import qualified Graphics.UI.GLUT as GL