34

Does there exist a Haskell graphics library or binding to an external library that fulfills the following requirements:

  1. Can be used from ghci, i.e. I don't have to link and restart the program.
  2. Works on MacOS X. (Tricky in conjunction with 1!)
  3. Can do simple vector graphics (lines, polygons, simple fills and strokes).
  4. 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".
Heinrich Apfelmus
  • 11,034
  • 1
  • 39
  • 67
  • Obviously there are several candidates: gtk/cairo; sdl; opengl; wx; qt; gd; ... the challenge is writing test programs for all of them, and then testing them on a Mac. – Don Stewart May 03 '11 at 17:36
  • 1
    Wx is known not to work in GHCi (problem with `libstdc++` or something). Sdl doesn't work either because it does some macro magic with `main`. I have tried OpenGL and GLFW, but I can invoke `main` only once. It's a mess, really. I envy Python, they managed to get a Tk binding that also works on the Mac. – Heinrich Apfelmus May 03 '11 at 18:01
  • I've had OpenVG working on Mac, but it fails point 1. Also OpenVG has no support for text rendering, whilst this is outside OpenVG's remit it is a fatal flaw to its adoption. It would be nice if there was a maintained Tk binding... – stephen tetley May 03 '11 at 19:23
  • FYI I wasn't able to get gtk to work from within ghci on OS X. I'm happy enough to compile though. – John L May 03 '11 at 21:33
  • @John: Which flavor of the gtk C libraries, X11 or Quartz? – Heinrich Apfelmus May 04 '11 at 05:51
  • ( for [X reference](http://stackoverflow.com/questions/14307789/cant-get-wxhaskell-to-work-from-ghci-on-mac/15850686#15850686) ) wx : now solved ghci -fno-ghci-sandbox works for me on OSX 10.8 , wx 0.90.0.1 thanks to Heinrich !! :https://github.com/jodonoghue/wxHaskell/pull/6 – Luc Taesch Apr 17 '13 at 05:14

5 Answers5

15

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!

  1. You need to invoke ghci with ghci -framework Carbon.
  2. 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.
  3. OpenGL has a 2D projection mode where you can draw lines and polygons.
  4. 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).

Sample bitmap

Heinrich Apfelmus
  • 11,034
  • 1
  • 39
  • 67
  • Thanks! Great example. Please note I had to fix two lines to make it work 'cause the library had been changed a bit. – Nek Jun 26 '13 at 16:19
  • I've done it already but it looks like you need to accept my changes somehow. – Nek Jun 27 '13 at 13:40
  • 1
    @Nek I dug around a bit and found your edit. Apparently, edits are reviewed by people other than the author and they rejected it because it was too minor or something. I will add your changes by hand. Thanks ! – Heinrich Apfelmus Jun 28 '13 at 07:20
  • That's strange someone rejected it 'cause it made the example work. :) ...but ok. Thanks for looking into this. – Nek Jun 28 '13 at 08:30
  • The enableGUI link is dead – SwiftsNamesake Nov 19 '17 at 02:08
3

The Gtk2Hs library fulfills all the requirements if you use the X11 version of the gtk2 framework.

Concerning the requirements:

  1. Using X11 avoids many problems.
  2. 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.)
  3. I would be surprised if GTK+ can't do that.
  4. 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
Heinrich Apfelmus
  • 11,034
  • 1
  • 39
  • 67
2

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)

carlosayam
  • 1,396
  • 1
  • 10
  • 15
1

Have you seen the GLFW as referenced http://plucky.cs.yale.edu/soe/software1.htm

Grady Player
  • 14,399
  • 2
  • 48
  • 76
1

More information on Haskell+GUI+OpenGL is available in this discussion: http://www.haskell.org/pipermail/haskell-cafe/2011-May/091991.html

amindfv
  • 8,438
  • 5
  • 36
  • 58