Does there exist a Haskell graphics library or binding to an external library that fulfills the following requirements:
- Can be used from
ghci
, i.e. I don't have to link and restart the program.
- Works on MacOS X. (Tricky in conjunction with 1!)
- Can do simple vector graphics (lines, polygons, simple fills and strokes).
- Can put bitmap images on screen. Example: blit a 17x12 .bmp image.
?
Please include a minimal source code example or a reference to it (just a window on screen, maybe with a green line drawn inside it) so that I can check the points 1. and 2. in particular. Also, if one of these feature requests is more elaborate (for example OpenGL + 4), please include a good reference.
PS: Concerning 1 and 2, I know about the enableGUI
trick and I am willing to use it. However, most libraries have the problem that you can't run the main
function multiple times and hence don't qualify.
Edit: To avoid wasting your time, here a list of packages that I've tried:
- wx - ghci chokes on
libstdc++
- sdl - redefines
main
to be a macro. Compile-time only.
- GLFW (OpenGL) - Can't run
main
twice, something about "failing because it can't install mouse event handler".
EDIT: Actually, I'm no longer sure. Several versions later, it seems that GLFW no longer works in GHCi on OS X.
It turns out that GLFW+OpenGL fulfills all four requirements!
- You need to invoke ghci with
ghci -framework Carbon
.
- You need the
EnableGUI.hs
file, which you can get here. Note that you can't load it right into GHCi, you have to comiple it, first.
- OpenGL has a 2D projection mode where you can draw lines and polygons.
- Bitmaps can be loaded as textures and put on polygons.
Here is a small example that puts a bitmap onto the screen. There are some restrictions on the bitmap: its dimensions must be a power of two (here 256) and it must be a .tga
file (here "Bitmap.tga"
). But since transparency is supported, this is not much of a problem.
You should be able to call main
multiple times without problem. The key point is that you should not call GLFW.terminate
.
import Graphics.Rendering.OpenGL as GL
import qualified Graphics.UI.GLFW as GLFW
import Graphics.Rendering.OpenGL (($=))
import Control.Monad
import EnableGUI
main = do
enableGUI
GLFW.initialize
-- open window
GLFW.openWindow (GL.Size 400 400) [GLFW.DisplayAlphaBits 8] GLFW.Window
GLFW.windowTitle $= "Bitmap Test"
-- enable alpha channel
GL.blend $= GL.Enabled
GL.blendFunc $= (GL.SrcAlpha, GL.OneMinusSrcAlpha)
-- set the color to clear background
GL.clearColor $= GL.Color4 0.8 0.8 0.8 0
-- set 2D orthogonal view inside windowSizeCallback because
-- any change to the Window size should result in different
-- OpenGL Viewport.
GLFW.windowSizeCallback $= \ size@(GL.Size w h) ->
do
GL.viewport $= (GL.Position 0 0, size)
GL.matrixMode $= GL.Projection
GL.loadIdentity
GL.ortho2D 0 (realToFrac w) (realToFrac h) 0
render <- initialize
loop render
GLFW.closeWindow
loop render = do
-- draw the entire screen
render
-- swap buffer
GLFW.swapBuffers
-- check whether ESC is pressed for termination
p <- GLFW.getKey GLFW.ESC
unless (p == GLFW.Press) $ do
-- sleep for 1ms to yield CPU to other applications
GLFW.sleep 0.001
-- only continue when the window is not closed
windowOpenStatus <- GLFW.getParam GLFW.Opened
unless (windowOpenStatus == False) $
loop render
-- rendering
initialize = do
-- load texture from file
GL.texture GL.Texture2D $= Enabled
[textureName] <- GL.genObjectNames 1
GL.textureBinding GL.Texture2D $= Just textureName
GL.textureFilter GL.Texture2D $= ((GL.Nearest, Nothing), GL.Nearest)
GLFW.loadTexture2D "Bitmap.tga" []
return $ do
GL.clear [GL.ColorBuffer]
GL.renderPrimitive GL.Quads $ do
GL.texCoord $ texCoord2 0 0
GL.vertex $ vertex3 (0) 256 0
GL.texCoord $ texCoord2 0 1
GL.vertex $ vertex3 (0) (0) 0
GL.texCoord $ texCoord2 1 1
GL.vertex $ vertex3 256 (0) 0
GL.texCoord $ texCoord2 1 0
GL.vertex $ vertex3 256 256 0
-- type signatures to avoid ambiguity
vertex3 :: GLfloat -> GLfloat -> GLfloat -> GL.Vertex3 GLfloat
vertex3 = GL.Vertex3
texCoord2 :: GLfloat -> GLfloat -> GL.TexCoord2 GLfloat
texCoord2 = GL.TexCoord2
color3 :: GLfloat -> GLfloat -> GLfloat -> GL.Color3 GLfloat
color3 = GL.Color3
Here an example bitmap (which you need to convert to .tga
).
![](https://www.manongdao.com/static/images/pcload.jpg)
The Gtk2Hs library fulfills all the requirements if you use the X11 version of the gtk2 framework.
Concerning the requirements:
- Using X11 avoids many problems.
- Install
gtk2
via MacPorts and use the +x11 option (default). (That said, I've had numerous problems installing gtk2 in the past, but this time it seemed to work.)
- I would be surprised if GTK+ can't do that.
- Ditto.
Here a minimal example
import Graphics.UI.Gtk
hello :: (ButtonClass o) => o -> IO ()
hello b = set b [buttonLabel := "Hello World"]
main :: IO ()
main = do
initGUI
window <- windowNew
button <- buttonNew
set window [windowDefaultWidth := 200, windowDefaultHeight := 200,
containerChild := button, containerBorderWidth := 10]
onClicked button (hello button)
onDestroy window mainQuit
widgetShowAll window
mainGUI
As of early 2014, I wasn't able to use @heinrich-apfelmus answer in Mac OS X. This GLFW-b example (link) however worked.
So, ensure you have:
$ cabal install glfw-b
and, if you tried Apfelmus' answer, you may need to
$ ghc-pkg list
$ ghc-pkg unregister GLFW-x.x.x.x
as both provide Graphics.UI.GLFW
, and you will get an "Ambiguous module name 'Graphics.UI.GLFW'" from ghc. Then I just tried the sample program above and it worked (Mac OS X, 10.9, Mavericks)
Have you seen the GLFW as referenced http://plucky.cs.yale.edu/soe/software1.htm
More information on Haskell+GUI+OpenGL is available in this discussion:
http://www.haskell.org/pipermail/haskell-cafe/2011-May/091991.html