~alcinnz/CatTrap

ref: bf8689d2214eb2b4046103fe8dae2d11bb212c04 CatTrap/app/Main.hs -rw-r--r-- 7.1 KiB
bf8689d2 — Adrian Cochrane Add descriptions of CSS property-parsing submodules. 1 year, 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
module Main where

import Text.XML.Light.Input (parseXMLDoc)
import qualified Text.XML.Light.Types as X
import Data.Maybe (fromJust, fromMaybe)
import qualified Data.Text as Txt
import Control.Monad (forM_, mapM)

import Graphics.Layout.CSS (CSSBox(..), finalizeCSS')
import Graphics.Layout.CSS.Font (placeholderFont)
import Graphics.Layout (LayoutItem, boxLayout,
                        layoutGetBox, layoutGetChilds, layoutGetInner)
import Graphics.Layout.Box (zeroBox)
import qualified Graphics.Layout.Box as B

import Stylist.Tree (StyleTree(..))
import Stylist (PropertyParser(..))
import Data.CSS.Syntax.Tokens (Token(..), tokenize)

import Graphics.UI.GLUT
import Graphics.GL.Core32

import Foreign.Ptr (castPtr, nullPtr)
import Foreign.Storable (Storable(..))
import Foreign.Marshal.Array (withArrayLen, allocaArray, peekArray)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Marshal.Utils (with)
import Foreign.C.String (withCString)

main :: IO ()
main = do
    (progname, args) <- getArgsAndInitialize
    source <- readFile $ case args of
        (filename:_) -> filename
        [] -> "styletree.xml"
    let xml = fromJust $ parseXMLDoc source
    let styles = xml2styles temp xml
    let layout = finalizeCSS' placeholderFont styles

    w <- createWindow progname

    vertexShader <- compileOGLShader vertexSource GL_VERTEX_SHADER
    fragmentShader <- compileOGLShader fragmentSource GL_FRAGMENT_SHADER
    shader <- compileOGLProgram [] [vertexShader, fragmentShader]
    glDetachShader shader vertexShader
    glDetachShader shader fragmentShader
    glDeleteShader vertexShader
    glDeleteShader fragmentShader

    displayCallback $= do
        clear [ ColorBuffer ]
        Size x y <- get windowSize
        let (display:_) = boxLayout zeroBox {
            B.size = B.Size (fromIntegral x) (fromIntegral y)
          } layout False

        glUseProgram shader
        attribScale <- withCString "windowsize" $ glGetUniformLocation shader
        glUniform3f attribScale (realToFrac x) (realToFrac y) 1

        renderDisplay shader display
        flush
    mainLoop

xml2styles :: CSSBox Nil -> X.Element -> StyleTree (CSSBox Nil)
xml2styles parent el = StyleTree {
    style = self',
    children = [xml2styles self' child | X.Elem child <- X.elContent el]
  } where self' = foldl (applyStyle parent) temp $ X.elAttribs el

applyStyle parent style (X.Attr (X.QName name _ _) val) =
    fromMaybe style $ longhand parent style (Txt.pack name) $
        filter (/= Whitespace) $ tokenize $ Txt.pack val

data Nil = Nil
instance PropertyParser Nil where
    temp = Nil
    inherit _ = Nil
    longhand _ _ _ _ = Nothing

renderDisplay :: GLuint -> LayoutItem Double Double ((Double, Double), a) -> IO ()
renderDisplay shader display = do
    let ((x, y), _) = layoutGetInner display
    let box = layoutGetBox display
    attribColour <- withCString "fill" $ glGetUniformLocation shader

    glUniform3f attribColour 1 0 0
    drawBox x y (B.width box) (B.height box)
    glUniform3f attribColour 0 1 0
    drawBox (x + B.left (B.margin box)) (y + B.top (B.margin box))
            (B.width box - B.left (B.margin box) - B.right (B.margin box))
            (B.height box - B.top (B.margin box) - B.bottom (B.margin box))
    glUniform3f attribColour 0 0 1
    drawBox (x + B.left (B.margin box) + B.left (B.border box))
            (y + B.top (B.margin box) + B.top (B.border box))
            (B.inline (B.size box) + B.left (B.padding box) + B.right (B.padding box))
            (B.block (B.size box) + B.top (B.padding box) + B.bottom (B.padding box))
    glUniform3f attribColour 1 1 0
    drawBox (x + B.left (B.margin box) + B.left (B.border box) + B.left (B.padding box))
            (y + B.top (B.margin box) + B.top (B.border box) + B.top (B.padding box))
            (B.inline $ B.size box) (B.block $ B.size box)

    mapM (renderDisplay shader) $ layoutGetChilds display
    return ()

drawBox x y width height = do
    buf <- withPointer $ glGenBuffers 1
    glBindBuffer GL_ARRAY_BUFFER buf
    glBufferData' GL_ARRAY_BUFFER [
        x, y, 0,
        x + width, y, 0,
        x, y + height, 0,

        x + width, y, 0,
        x + width, y + height, 0,
        x, y + height, 0
      ] GL_STATIC_DRAW

    glEnableVertexAttribArray 0
    glBindBuffer GL_ARRAY_BUFFER buf
    glVertexAttribPointer 0 3 GL_FLOAT GL_FALSE 0 nullPtr

    glDrawArrays GL_TRIANGLES 0 6
    glDisableVertexAttribArray 0

withPointer cb = alloca $ \ret' -> do
    cb ret'
    peek ret'

glBufferData' _ [] _ = return ()
glBufferData' target dat usage =
    withArrayLen (map realToFrac dat :: [Float]) $ \len dat' -> do
        glBufferData target (toEnum $ len*sizeOf (head dat)) (castPtr dat') usage

compileOGLShader :: String -> GLenum -> IO GLuint
compileOGLShader src shType = do
  shader <- glCreateShader shType
  if shader == 0
    then error "Could not create shader"
    else do
      success <-do
        withCString (src) $ \ptr ->
          with ptr $ \ptrptr -> glShaderSource shader 1 ptrptr nullPtr

        glCompileShader shader
        with (0 :: GLint) $ \ptr -> do
          glGetShaderiv shader GL_COMPILE_STATUS ptr
          peek ptr

      if success == GL_FALSE
        then do
          err <- do
            infoLog <- with (0 :: GLint) $ \ptr -> do
                glGetShaderiv shader GL_INFO_LOG_LENGTH ptr
                logsize <- peek ptr
                allocaArray (fromIntegral logsize) $ \logptr -> do
                    glGetShaderInfoLog shader logsize nullPtr logptr
                    peekArray (fromIntegral logsize) logptr

            return $ unlines [ "Could not compile shader:"
                             , src
                             , map (toEnum . fromEnum) infoLog
                             ]
          error err
        else return shader

compileOGLProgram :: [(String, Integer)] -> [GLuint] -> IO GLuint
compileOGLProgram attribs shaders = do
  (program, success) <- do
     program <- glCreateProgram
     forM_ shaders (glAttachShader program)
     forM_ attribs
       $ \(name, loc) ->
         withCString name
           $ glBindAttribLocation program
           $ fromIntegral loc
     glLinkProgram program

     success <- with (0 :: GLint) $ \ptr -> do
       glGetProgramiv program GL_LINK_STATUS ptr
       peek ptr
     return (program, success)

  if success == GL_FALSE
  then with (0 :: GLint) $ \ptr -> do
    glGetProgramiv program GL_INFO_LOG_LENGTH ptr
    logsize <- peek ptr
    infoLog <- allocaArray (fromIntegral logsize) $ \logptr -> do
      glGetProgramInfoLog program logsize nullPtr logptr
      peekArray (fromIntegral logsize) logptr
    error $ unlines
          [ "Could not link program"
          , map (toEnum . fromEnum) infoLog
          ]
  else do
    forM_ shaders glDeleteShader
    return program

vertexSource = unlines [
    "#version 330 core",
    "layout(location = 0) in vec3 vertexPositionModelSpace;",
    "uniform vec3 windowsize;",
    "void main() {",
    "gl_Position.xyz = vertexPositionModelSpace/windowsize - 1;",
    "gl_Position.y = -gl_Position.y;",
    "gl_Position.w = 1.0;",
    "}"
  ]
fragmentSource = unlines [
    "#version 330 core",
    "uniform vec3 fill;",
    "out vec3 colour;",
    "void main() { colour = fill; }"
  ]