Графическая программа Haskell закрывается слишком рано

Я пишу программу, используя OpenGl и Haskell, которая должна рисовать прямоугольник, когда и где щелкают мышью. Однако программа закрывается, как только я нажимаю и до того, как прямоугольник будет нарисован.

import Graphics.Rendering.OpenGL
import Graphics.UI.GLUT
import Graphics.UI.GLUT.Callbacks.Window

main = do
  (progname, _) <- getArgsAndInitialize
  createWindow progname
  keyboardMouseCallback $= Just myKeyboardMouseCallback
  displayCallback $= display
  mainLoop

myKeyboardMouseCallback key keyState modifiers (Position x y) =
  case (key, keyState) of
    (MouseButton LeftButton, Down) -> do
      clear[ColorBuffer]
      let x = x :: GLfloat
      let y = y :: GLfloat
      renderPrimitive Quads $ do
        color $ (Color3 (1.0::GLfloat) 0 0)
        vertex $ (Vertex3 (x::GLfloat) y 0)
        vertex $ (Vertex3 (x::GLfloat) (y+0.2) 0)
        vertex $ (Vertex3 ((x+0.2)::GLfloat) (y+0.2) 0)
        vertex $ (Vertex3 ((x+0.2)::GLfloat) y 0)
      flush
    _ -> return ()

display = do
  clear [ColorBuffer]
  renderPrimitive Lines $ do
  flush

Есть ли что-то, вызывающее преждевременное завершение программы в одном из методов, или это просто компьютерный способ сказать мне, что я не могу этого сделать?


person lewdsterthumbs    schedule 16.04.2012    source источник


Ответы (1)


Вы не можете делать то, что пытаетесь сделать. В программе OpenGL вам разрешено выполнять команды рисования только в контексте OpenGL. Этот контекст всегда привязан к определенному потоку и активен только в теле displayCallback в GLUT, потому что другие обратные вызовы потенциально могут выполняться из других потоков.

Однако вы можете сказать: на многих/большинстве платформ отдельный поток не используется для ввода в GLUT, а это означает, что вы теоретически можете выполнять там команды рисования. Однако есть много других вещей, которые играют роль в том, где и когда вы можете отдавать команды рисования; например, когда среда требует, чтобы вы использовали вывод с двойной буферизацией, когда буферы должны очищаться очень специфическими способами (например, при использовании EGL или GLX для X11).

Вкратце: вы не должны выдавать команды рисования за пределами файла displayCallback. Вся причина его существования заключается в том, что вы можете позволить GLUT обрабатывать специфичные для платформы вещи, связанные с собственным управлением буфером кадров, и он ожидает, что вы будете хранить свой код в нужных местах для его работы.

Вместо этого вы хотите создать изменяемую переменную (эй, вы используете OpenGL; изменяемое состояние не должно вас беспокоить), которая указывает, следует ли рисовать прямоугольник и где. Что-то вроде (с использованием Data.IORef):

main = do
  -- ...

  -- Create a mutable variable that stores a Bool and a pair of floats
  mouseStateRef <- newIORef (False, (0, 0))

  -- Pass a reference to the mutable variable to the callbacks
  keyboardMouseCallback $= Just (myKeyboardMouseCallback mouseStateRef)
  displayCallback $= (display mouseStateRef)

myKeyboardMouseCallback mouseStateRef key keyState modifiers (Position x y) =
  case key of
    MouseButton LeftButton -> do
      -- Store the current mouse pressed state and coords in the reference
      writeIORef mouseStateRef (keyState == Pressed, (x, y))
    _ -> return ()

display mouseStateRef = do
  clear [ColorBuffer]

  -- Read the state stored in the mutable reference
  (pressed, (x, y)) <- readIORef mouseStateRef

  -- Draw the quad if the mouse button is pressed
  when pressed . renderPrimitive Quads $ do
    color $ (Color3 (1.0::GLfloat) 0 0)
    vertex $ (Vertex3 (x::GLfloat) y 0)
    vertex $ (Vertex3 (x::GLfloat) (y+0.2) 0)
    vertex $ (Vertex3 ((x+0.2)::GLfloat) (y+0.2) 0)
    vertex $ (Vertex3 ((x+0.2)::GLfloat) y 0)
  flush
person dflemstr    schedule 17.04.2012
comment
В этом есть смысл, но когда я попытался это сделать, я получил ошибки из-за того, что iorefs выходит за рамки, а еще одну - в keyState == Pressed, а именно в Pressed. - person lewdsterthumbs; 17.04.2012
comment
Я не тестировал код. Я просто хотел дать вам общее представление. Вы должны импортировать Data.IORef и некоторые другие вещи. Я не буду делать всю работу за вас. - person dflemstr; 18.04.2012
comment
Я знаю, но у меня все еще есть некоторые из тех же проблем. MouseStateRef на самом деле содержит пару целых чисел. Мне нужны поплавки. - person lewdsterthumbs; 24.04.2012
comment
Верно; используйте fromIntegral для преобразования целочисленного значения в число с плавающей запятой/что-то еще. - person dflemstr; 24.04.2012
comment
Нет, что-то вроде let x1 = fromIntegral x. Тогда x1 может иметь тип GLfloat. - person dflemstr; 24.04.2012
comment
Также вызов display MouseStateRef означает, что отображение работает только при щелчке мыши? - person lewdsterthumbs; 24.04.2012
comment
Нет, это означает только то, что обратный вызов дисплея имеет доступ к этой переменной при запуске. Это часть when pressed, которая проверяет щелчок мышью перед рендерингом. - person dflemstr; 24.04.2012
comment
Я не думал, что when это реально. Или я должен сделать оператор if для этого? - person lewdsterthumbs; 24.04.2012
comment
when функция. - person dflemstr; 25.04.2012
comment
Хорошо, я думал, что это псевдокод, потому что я получал ошибку не в области видимости. - person lewdsterthumbs; 27.04.2012