packages feed

glfw-group-0.1.0.6: src/Graphics/UI/GlfwG/Window.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments, LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Graphics.UI.GlfwG.Window (

	-- * CREATE AND DESTROY

	W, create, Group, group, create', unsafeDestroy, lookup,

	shouldClose, setShouldClose,

	-- ** HINT

	hint, B.WindowHint(..),
	B.ClientAPI(..),

	-- * PARAMETER

	getSize, setSize,
	getFrameSize,
	getFramebufferSize,

	-- * CALLBACK

	setKeyCallback, KeyCallback,
	setFramebufferSizeCallback, B.FramebufferSizeCallback,

	-- * STATE

	getKey,

	-- * ATTRIBUTES
	
	getIconified

	) where

import Prelude hiding (lookup)

import Control.Concurrent.STM
import Control.Concurrent.STM.TSem
import Data.Map qualified as M

import Graphics.UI.GLFW qualified as B
import Graphics.UI.GlfwG.Window.Type

data Group s k = Group TSem (TVar (M.Map k (W s)))

create :: Int -> Int -> String -> Maybe B.Monitor -> Maybe B.Window ->
	(forall s . W s -> IO a) -> IO a
create wd hg ttl mm mws f = group \g -> f . fromRight =<< create' g () wd hg ttl mm mws
	where fromRight = \case Left _ -> error "never occur"; Right w -> w

group :: (forall s . Group s k -> IO a) -> IO a
group f = do
	(sem, m) <- atomically $ (,) <$> newTSem 1 <*> newTVar M.empty
	rtn <- f $ Group sem m
	((\(W w) -> B.destroyWindow w) `mapM_`) =<< atomically (readTVar m)
	pure rtn

create' :: Ord k => Group s k -> k -> Int -> Int -> String ->
	Maybe B.Monitor -> Maybe B.Window -> IO (Either String (W s))
create' (Group sem ws) k wd hg ttl mm mws = do
	ok <- atomically do
		mx <- M.lookup k <$> readTVar ws
		case mx of
			Nothing -> waitTSem sem >> pure True
			Just _ -> pure False
	if ok
	then do	mw <- (W <$>) <$> B.createWindow wd hg ttl mm mws
		case mw of
			Just w -> atomically do
				modifyTVar ws (M.insert k w) 
				signalTSem sem
				pure $ Right w
			Nothing -> pure . Left $
				"Gpu.Vulkan.Khr.Surface." ++
				"Glfw.Window.create': GLFW-b: error"
	else pure . Left $ "Gpu.Vulkan.Khr.Surface.Glfw.Window.create': " ++
		"The key already exist"

unsafeDestroy :: Ord k => Group s k -> k -> IO (Either String ())
unsafeDestroy (Group sem ws) k = do
	mw <- atomically do
		mx <- M.lookup k <$> readTVar ws
		case mx of
			Nothing -> pure Nothing
			Just _ -> waitTSem sem >> pure mx
	case mw of
		Nothing -> pure . Left $
			"Gpu.Vulkan.Khr.Surface.Glfw.Window.destroy: " ++
			"No such key"
		Just (W w) -> do
			B.destroyWindow w
			atomically do
				modifyTVar ws (M.delete k)
				signalTSem sem
				pure $ Right ()

hint :: B.WindowHint -> IO ()
hint = B.windowHint

lookup :: Ord k => Group s k -> k -> IO (Maybe (W s))
lookup (Group _sem ws) k = atomically $ M.lookup k <$> readTVar ws

setFramebufferSizeCallback :: W s -> Maybe B.FramebufferSizeCallback -> IO ()
setFramebufferSizeCallback (W w) = B.setFramebufferSizeCallback w

getSize :: W sw -> IO (Int, Int)
getSize (W w) = B.getWindowSize w

setSize :: W sw -> Int -> Int -> IO ()
setSize (W w) = B.setWindowSize w

getFrameSize :: W sw -> IO (Int, Int, Int, Int)
getFrameSize (W w) = B.getWindowFrameSize w

getFramebufferSize :: W sw -> IO (Int, Int)
getFramebufferSize (W w) = B.getFramebufferSize w

setKeyCallback :: W s -> Maybe (KeyCallback s) -> IO ()
setKeyCallback (W w) = B.setKeyCallback w . ((. W) <$>)

type KeyCallback s = W s -> B.Key -> Int -> B.KeyState -> B.ModifierKeys -> IO ()

shouldClose :: W sw -> IO Bool
shouldClose (W w) = B.windowShouldClose w

setShouldClose :: W sw -> Bool -> IO ()
setShouldClose (W w) = B.setWindowShouldClose w

getKey :: W sw -> B.Key -> IO B.KeyState
getKey (W w) = B.getKey w

getIconified :: W sw -> IO Bool
getIconified (W w) = B.getWindowIconified w