diff --git a/.gitmodules b/.gitmodules index 62b10e6..4d1e814 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "PortMidi"] path = PortMidi url = git@github.com:yosukeueda33/PortMidi-haskell.git +[submodule "vty-windows"] + path = vty-windows + url = https://github.com/chhackett/vty-windows.git diff --git a/README.md b/README.md index e70cdb5..bf37fc1 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,17 @@ If you built from the souce, execute this command in the cloned dir. make run ``` +If you get this output, +```bash +Setup of the rendering thread failed: Unable to make GL context current +Falling back to rendering in the main thread. The content may not be updated while resizing the window. +``` +this may fix the problem. +```bash +__GLX_VENDOR_LIBRARY_NAME=mesa LIBGL_ALWAYS_SOFTWARE=1 ./chordMapper-x86_64.AppImage +__GLX_VENDOR_LIBRARY_NAME=mesa LIBGL_ALWAYS_SOFTWARE=1 make run +``` + This is the UI of the chordMapper. Sorry, it's still awful. Select input and output device that you want to use. And then, click `Play/Stop` button. diff --git a/package.yaml b/package.yaml index 16d8a19..4151a2f 100644 --- a/package.yaml +++ b/package.yaml @@ -28,17 +28,21 @@ dependencies: - PortMidi - deepseq - HCodecs -- unix - stm - time - containers -- monomer - text - text-show - lens - dhall - filepath - directory +- brick +- microlens +- microlens-th +- microlens-mtl +- vty +- vector ghc-options: - -Wall diff --git a/src/Mapper.hs b/src/Mapper.hs index 3f2e4c6..1d77377 100644 --- a/src/Mapper.hs +++ b/src/Mapper.hs @@ -45,7 +45,7 @@ import Chord (Chord(..) , getVoicingBetweenOn, getEnvelopeDifference , degreeToChord7thOnePassingTension , addNoMin2ndTension) -import Ui +import Tui import Types import ArgParse import Rec @@ -120,7 +120,7 @@ mapperMain = do checkFilePath "font" fontPath -- Load config. - config <- Dhall.input Dhall.auto $ T.pack cfgPath :: IO FullConfig + config <- Dhall.inputFile Dhall.auto cfgPath :: IO FullConfig print config -- Initialize buffers. @@ -135,7 +135,7 @@ mapperMain = do in bimap f f <$> getAllDevices :: IO ([(InputDeviceID, String)], [(OutputDeviceID, String)]) -- UI thread. let needOutSubDev = isMinilab3 config - (uiInput, uiUpdator) <- createUiThread devices needOutSubDev $ T.pack fontPath + (uiInput, uiUpdator) <- createTuiThread devices needOutSubDev initializeMidi -- Wait and execute UI input. let @@ -148,12 +148,12 @@ mapperMain = do --Start Midi threads. _ <- forkIO $ mainLoop loopExitSig loopExitDoneSig uiUpdator config inDev outDev mbOutSubDev - putStrLn "Loop Started." + -- putStrLn "Loop Started." loop -- Keep listening. UiStop -> do putMVar loopExitSig () takeMVar loopExitDoneSig - putStrLn "Loop Stop Done." + -- putStrLn "Loop Stop Done." putMVar loopExitDoneSig () loop -- Keep listening. UiExit -> do @@ -181,14 +181,14 @@ mainLoop exitSig exitDoneSig uiUpdator config inDev outDev mbOutSubDev = do tChordStep <- newTVarIO 0 preStopSig <- newTVarIO False stopSig <- newTVarIO False - putStrLn "Clearing MIDI Devices..." + -- putStrLn "Clearing MIDI Devices..." wait 0.5 (getRecData, recorder, getMsgToStop, recStart, recPlayResume, recPlayStop) <- createRecPlay tChordMap (fromIntegral $ recStepNum config) let qnSec = oneQnSec config op = do - putStrLn "Initializing MIDI Devices..." + -- putStrLn "Initializing MIDI Devices..." _ <- forkIO $ clockLoop qnSec tChordMapSet tChordMap genBuf preStopSig tChordStep uiUpdator (fromIntegral $ clockOffset config) @@ -208,19 +208,19 @@ mainLoop exitSig exitDoneSig uiUpdator config inDev outDev mbOutSubDev = do tPushingKeys tChordMap stopSig -- poll input and add to buffer _ <- forkIO $ controlReceiver stopSig tControl -- For Special Input [ ( NextChordMapSet - , putStrLn "Chord map change registered!" - >> changeChordMapSet + , -- putStrLn "Chord map change registered!" + changeChordMapSet tChordMapSet tChordMapSetIndex (chordMapSetList config)) , ( RecStart - , putStrLn "Rec Start!" - >> recStart) + , -- putStrLn "Rec Start!" + recStart) , ( RecPlayResume - , putStrLn "Rec Play Resume!" - >> recPlayResume) + , -- putStrLn "Rec Play Resume!" + recPlayResume) , ( RecPlayStop - , putStrLn "Rec Play Stop!" - >> recPlayStop + , -- putStrLn "Rec Play Stop!" + recPlayStop >> atomically ( readTVar tChordStep >>= getMsgToStop @@ -244,21 +244,21 @@ mainLoop exitSig exitDoneSig uiUpdator config inDev outDev mbOutSubDev = do return (length vals) mergedSendOut ellapsed = sum <$> mapM ($ ellapsed) [sendOut, sendSubOut] _ <- forkIO (midiOutRec 0.0 mergedSendOut stopSig) -- take from buffer and output - putStrLn "MIDI I/O services started." + -- putStrLn "MIDI I/O services started." detectExitLoop stopSig -- should only exit this via handleCtrlC closeOp = do atomically $ writeTVar preStopSig True -- signal the other threads to stop - putStrLn "Stopping clock signal" -- not clear why Ctrl+C is needed again + -- putStrLn "Stopping clock signal" -- not clear why Ctrl+C is needed again wait 1.0 atomically $ writeTVar stopSig True -- signal the other threads to stop - putStrLn "Stopping MIDI devices" -- not clear why Ctrl+C is needed again + -- putStrLn "Stopping MIDI devices" -- not clear why Ctrl+C is needed again wait 2.0 -- give the other threads time to stop before closing down MIDI! - putStrLn "before terminating..." + -- putStrLn "before terminating..." wait 0.5 -- give MIDI time to close down putMVar exitDoneSig () _ <- forkIO op takeMVar exitSig - putStrLn "Got exit" + -- putStrLn "Got exit" closeOp -- Invoke tasks by special inputs like tapping pads. @@ -370,7 +370,7 @@ clockLoop qnSec tChordMapSet tChordMap genBuf preStopSig tChordStep uiUpdator _ <- execStateT loop 0 sendStop uiUpdator (Just "", Just 0, Just 0) - putStrLn "closed clock" + -- putStrLn "closed clock" type ChordMap = (Int, String, ChordKeyMap) @@ -510,8 +510,8 @@ specialInput tControl cfgs msg = isSameNoteOn _ _ = False in when (isSameNoteOn cfgMsg msg) $ do - putStrLn "got special input." - print msg + -- putStrLn "got special input." + -- print msg atomically . writeTVar tControl $ Just control in mapM_ f cfgs @@ -631,4 +631,4 @@ sendMidiOut dev ms = outputMidi dev >> mapM_ (deliverMidiEvent dev . (0,) . snd) type Seconds = Double -- Wait specified seconds. wait :: Seconds -> IO () -wait s = threadDelay $ round $ s * 1000000 \ No newline at end of file +wait s = threadDelay $ round $ s * 1000000 diff --git a/src/Tui.hs b/src/Tui.hs new file mode 100644 index 0000000..303b254 --- /dev/null +++ b/src/Tui.hs @@ -0,0 +1,197 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE TemplateHaskell #-} + +module Tui where + +import Data.Bool (bool) +import Data.Maybe (fromJust) +import Control.Concurrent +import Control.Concurrent.STM +import qualified Data.List as L +import qualified Data.Vector as Vec +import Lens.Micro ((^.)) +import Lens.Micro.TH (makeLenses) +import Lens.Micro.Mtl +import Control.Monad (void) +import Control.Monad.Trans (liftIO) +#if !(MIN_VERSION_base(4,11,0)) +import Data.Monoid ((<>)) +#endif +import qualified Graphics.Vty as V + +import qualified Brick.Types as T +import Brick.AttrMap +import Brick.Util +import Brick.Types (Widget, ViewportType(Vertical)) +import qualified Brick.Main as M +import qualified Brick.Widgets.Edit as E +import qualified Brick.Widgets.Center as C +import qualified Brick.Widgets.Border as B +import Brick.Widgets.Core +import qualified Brick.Widgets.List as WL +import Euterpea.IO.MIDI.MidiIO + ( OutputDeviceID, InputDeviceID, unsafeInputID, unsafeOutputID ) +import Brick.BChan (BChan, newBChan, writeBChan) + +data UiInput + = UiStart InputDeviceID OutputDeviceID + (Maybe OutputDeviceID) -- For like, lights on keyboard like MiniLab3 touch pads. + | UiStop + | UiExit + +data CustomEvent = ChangeChordInfo String Int Int + +type UiUpdator = (Maybe String, Maybe Int, Maybe Int) -> IO () +type DeviceLists = ([(InputDeviceID, String)], [(OutputDeviceID, String)]) + +data Name = InputList | OutputList | SpecialList | StartStopButton + deriving (Eq, Ord, Show) + +data St = St { _chordName :: String + , _clockProgress :: Int + , _chordSetProgress :: Int + , _selectListIndex :: Int + , _inputList :: WL.List Name String + , _outputList :: WL.List Name String + , _specialList :: WL.List Name String + , _playing :: Bool + } + +makeLenses ''St + +drawUi :: St -> [Widget Name] +drawUi st = L.singleton + $ hBox [ + colHigh 0 (str "Select Input:") <=> + WL.renderList renderFunc True (st^.inputList) + , colHigh 1 (str "Select Output:") <=> + WL.renderList renderFunc True (st^.outputList) + , colHigh 2 (str "Select Special output:") <=> + WL.renderList renderFunc True (st^.specialList) + ] <=> + strWrap ( "Chord : " ++ st^.chordName ) <=> + strWrap ( "Chord set progress: " + ++ concat (replicate (st^.chordSetProgress) "o")) <=> + strWrap ( "Chord progress : " + ++ concat (replicate (st^.clockProgress `div` 6) "o")) <=> + (withAttr (attrName "info") + . strWrap $ "Left/Right: Select device type." + ++ " Up/Down: Select device." + ++ " Space: Start/Stop playing.") + where + colHigh i = if i == st^.selectListIndex then withAttr (attrName "colHighlight") else id + renderFunc sel e = rowHigh $ strWrap e + where rowHigh = if sel then withAttr (attrName "rowHighlight") else id + +appEvent :: (Int -> Int -> Int -> IO ()) + -> IO () -> T.BrickEvent Name CustomEvent -> T.EventM Name St () +appEvent _ _ (T.AppEvent (ChangeChordInfo name clockI setI)) + = chordName %= const name + >> clockProgress %= const clockI + >> chordSetProgress %= const setI +appEvent start stop (T.VtyEvent e) = case e of + (V.EvKey V.KLeft []) -> selectListIndex %= (\i -> max 0 (i - 1)) + (V.EvKey V.KRight []) -> selectListIndex %= (\i -> min 2 (i + 1)) + (V.EvKey (V.KChar ' ') []) -> + use playing >>= bool + (do + inId <- use $ inputList . WL.listSelectedL + outId <- use $ outputList . WL.listSelectedL + spId <- use $ specialList . WL.listSelectedL + liftIO $ start (fromJust inId) + (fromJust outId) + (fromJust spId) + playing %= const True) + (liftIO stop >> playing %= const False) + (V.EvKey V.KEsc []) -> do + vty <- M.getVtyHandle + liftIO $ V.setMode (V.outputIface vty) V.Mouse False + liftIO $ putStr "\ESC[0m\ESC[2J\ESC[H" + M.halt + ev -> do + nowCol <- use selectListIndex + let targetList = case nowCol of + 0 -> inputList + 1 -> outputList + 2 -> specialList + _ -> inputList + T.zoom targetList $ WL.handleListEvent ev +appEvent _ _ _ = return () + +aMap :: AttrMap +aMap = attrMap V.defAttr + [ (attrName "colHighlight", V.white `on` V.cyan) + , (attrName "rowHighlight", V.white `on` V.blue) + , (attrName "info", V.white `on` V.magenta) + ] + +app :: (Int -> Int -> Int -> IO ()) -> IO () -> M.App St CustomEvent Name +app start stop = + M.App { M.appDraw = drawUi + , M.appChooseCursor = M.showFirstCursor + , M.appStartEvent = do + vty <- M.getVtyHandle + liftIO $ V.setMode (V.outputIface vty) V.Mouse True + , M.appHandleEvent = appEvent start stop + , M.appAttrMap = const aMap + } + +tuiMain :: DeviceLists + -> (Int -> Int -> Int -> IO ()) -> IO () -> IO () + -> BChan CustomEvent + -> IO() +tuiMain devices start stop exit varsChan = do + let + inputs = Vec.fromList . map (\(id, name) -> show id ++ ": " ++ name) $ fst devices + outputs = Vec.fromList . map (\(id, name) -> show id ++ ": "++ name) $ snd devices + specials = outputs + _ <- M.customMainWithDefaultVty (Just varsChan) (app start stop) + $ St { _chordName = "" + , _clockProgress = 0 + , _chordSetProgress = 0 + , _selectListIndex = 0 + , _inputList = WL.list InputList inputs 5 + , _outputList = WL.list OutputList outputs 5 + , _specialList = WL.list SpecialList specials 5 + , _playing = False + } + exit + +readVars :: BChan CustomEvent -> TVar String -> TVar Int -> TVar Int -> IO () +readVars varsChan tChordName tClockProgress tChordSetProgress = go + where + go = do + threadDelay 100000 + writeBChan varsChan =<< ChangeChordInfo <$> readTVarIO tChordName + <*> readTVarIO tClockProgress + <*> readTVarIO tChordSetProgress + go + + +createTuiThread :: DeviceLists -> Bool -> IO (IO UiInput, UiUpdator) +createTuiThread devices needOutSubDev = do + mUiInput <- newEmptyMVar + tChordName <- newTVarIO "" + tClockProgress <- newTVarIO 0 + tChordSetProgress <- newTVarIO 0 + varsChan <- newBChan 10 + void . forkIO $ readVars varsChan tChordName tClockProgress tChordSetProgress + _ <- forkIO $ tuiMain devices + (\inIx outIx spIx -> + let + inId = fst $ fst devices !! inIx + outId = fst $ snd devices !! outIx + mSpId = Just . fst $ snd devices !! spIx + in putMVar mUiInput $ UiStart inId outId mSpId) + (putMVar mUiInput UiStop) + (putMVar mUiInput UiExit) + varsChan + let + uiInput = takeMVar mUiInput + updateTVar tx = maybe (return ()) (atomically . writeTVar tx) + updator (mbChordName, mbClockProgress, mbChordSetProgress) + = updateTVar tChordName mbChordName + >> updateTVar tClockProgress mbClockProgress + >> updateTVar tChordSetProgress mbChordSetProgress + + return (uiInput, updator) diff --git a/src/Ui.hs b/src/Ui.hs deleted file mode 100644 index 244b1cc..0000000 --- a/src/Ui.hs +++ /dev/null @@ -1,179 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} - -module Ui (UiInput(..), UiUpdator, createUiThread) where - -import Control.Lens -import qualified Data.Text as T -import Monomer - -import Control.Concurrent.MVar -import Control.Concurrent.STM -import Control.Concurrent (forkIO, threadDelay) - -import Euterpea.IO.MIDI.MidiIO - ( OutputDeviceID, InputDeviceID, unsafeInputID, unsafeOutputID ) - - -data PlayState = PlayStopped | PlayStopping | Playing deriving (Eq, Show) - -type DeviceLists = ([(InputDeviceID, String)], [(OutputDeviceID, String)]) - -data AppModel = AppModel { - _chordName :: String, - _playing :: PlayState, - _clockProgress :: Int, - _chordSetProgress :: Int, - _inDev :: (InputDeviceID, String), - _outDev :: (OutputDeviceID, String), - _outSubDev :: (OutputDeviceID, String) -} deriving (Eq, Show) - -data UiOutput = UiOutput { - tChordName :: TVar String, - tClockProgress :: TVar Int, - tChordSetProgress :: TVar Int -} - -type UiUpdator = (Maybe String, Maybe Int, Maybe Int) -> IO () - -data AppEvent - = AppInit - | AppStartStop - | AppStartDone - | AppStopDone - | AppSetBorder [Int] - | AppDispose - | AppDisposeDone - | AppExit - | AppExitDone - | AppUpdateChord String - | AppUpdateClockProgress Int - | AppUpdateChordSetProgress Int - deriving (Eq, Show) - -data UiInput - = UiStart InputDeviceID OutputDeviceID - (Maybe OutputDeviceID) -- For like, lights on keyboard like MiniLab3 touch pads. - | UiStop - | UiExit - -makeLenses 'AppModel - -when' :: Bool -> [a] -> [a] -when' True xs = xs -when' False _ = [] - -buildUI - :: DeviceLists - -> Bool - -> WidgetEnv AppModel AppEvent - -> AppModel - -> WidgetNode AppModel AppEvent -buildUI devices needOutSubDev _ model = widgetTree where - widgetTree = vstack ([ - label_ (T.pack $ model ^. chordName) [resizeFactor 1] `styleBasic` [textSize 24], - label_ (T.pack . genProgressString 1 $ model ^. chordSetProgress) [resizeFactor 1] `styleBasic` [textSize 24], - label_ (T.pack . genProgressString 6 $ model ^. clockProgress) [resizeFactor 1] `styleBasic` [textSize 24] - ] - ++ devLists - ++ [ - button "Play/Stop" AppStartStop - ]) `styleBasic` [padding 10] - devLists = [ - label_ "Select input device:" [] - , selectList inDev (fst devices) makeListLine - , label_ "Select output device:" [] - , selectList outDev (snd devices) makeListLine - ] - ++ when' needOutSubDev [ label_ "Select special output device:" [] - , selectList outSubDev (snd devices) makeListLine] - makeListLine (iD, name) = label . T.pack $ show iD ++ " " ++ name - -genProgressString :: Int -> Int -> String -genProgressString divNum x - | x >= 0 = concat . flip replicate "o" $ x `div` divNum - | otherwise = "" - -handleEvent - :: MVar UiInput -> UiOutput - -> Bool - -> WidgetEnv AppModel AppEvent - -> WidgetNode AppModel AppEvent - -> AppModel - -> AppEvent - -> [AppEventResponse AppModel AppEvent] -handleEvent mUiInput uiOutput needOutSubDev _ _ model evt = - let - startTask = let i = fst $ model ^. inDev - o = fst $ model ^. outDev - mbSubOut = if needOutSubDev then Just (fst $ model ^. outSubDev) else Nothing - cmd = UiStart i o mbSubOut - in putMVar mUiInput cmd >> return AppStartDone - stopTask = putMVar mUiInput UiStop >> return AppStopDone - disposeTask = return AppDisposeDone - exitTask = putMVar mUiInput UiExit >> return AppExitDone - in case evt of - AppInit -> [Producer $ outputUpdateProducer uiOutput] - AppStartStop -> case model ^. playing of - Playing -> [ Task stopTask - , Model $ model & playing .~ PlayStopping - , Model $ model & chordName .~ "" - ] - PlayStopped -> [Task startTask, Model $ model & playing .~ Playing] - PlayStopping -> [] - AppStopDone -> [Model $ model & playing .~ PlayStopped] - AppDispose -> [Task disposeTask] - AppExit -> [Task exitTask] - AppUpdateChord name -> [Model $ model & chordName .~ name] - AppUpdateClockProgress x -> [Model $ model & clockProgress .~ x] - AppUpdateChordSetProgress x -> [Model $ model & chordSetProgress .~ x] - _ -> [] - -outputUpdateProducer :: UiOutput -> (AppEvent -> IO ()) -> IO () -outputUpdateProducer uiOutput sendMsg = do - let f event elm = readTVarIO (elm uiOutput) >>= sendMsg . event - f AppUpdateChord tChordName - f AppUpdateClockProgress tClockProgress - f AppUpdateChordSetProgress tChordSetProgress - threadDelay 10000 - outputUpdateProducer uiOutput sendMsg - -uiMain :: MVar UiInput -> UiOutput -> DeviceLists -> Bool -> T.Text -> IO () -uiMain mUiInput uiOutput devices needOutSubDev fontPath = do - startApp model h b config - where - h = handleEvent mUiInput uiOutput needOutSubDev :: AppEventHandler AppModel AppEvent - b = buildUI devices needOutSubDev :: AppUIBuilder AppModel AppEvent - config = [ - appWindowTitle "KANNASHI chordMapper", - appTheme darkTheme, - appFontDef "Regular" fontPath, - appInitEvent AppInit, - appDisposeEvent AppDispose, - appExitEvent AppExit - ] - model = AppModel "" PlayStopped 0 0 - (unsafeInputID 0, "") (unsafeOutputID 0, "") - (unsafeOutputID 0, "") :: AppModel - - -createUiThread :: DeviceLists -> Bool -> T.Text -> IO (IO UiInput, UiUpdator) -createUiThread devices needOutSubDev fontPath = do - mUiInput <- newEmptyMVar - tChordName' <- newTVarIO "" - tClockProgress' <- newTVarIO 0 - tChordSetProgress' <- newTVarIO 0 - _ <- forkIO $ uiMain mUiInput - (UiOutput tChordName' tClockProgress' tChordSetProgress') - devices needOutSubDev fontPath - - let - uiInput = takeMVar mUiInput - updateTVar tx = maybe (return ()) (atomically . writeTVar tx) - updator (mbChordName, mbClockProgress, mbChordSetProgress) - = updateTVar tChordName' mbChordName - >> updateTVar tClockProgress' mbClockProgress - >> updateTVar tChordSetProgress' mbChordSetProgress - - return (uiInput, updator) \ No newline at end of file diff --git a/stack-win.yaml b/stack-win.yaml new file mode 100644 index 0000000..3ac3088 --- /dev/null +++ b/stack-win.yaml @@ -0,0 +1,80 @@ +# This file was automatically generated by 'stack init' +# +# Some commonly used options have been documented as comments in this file. +# For advanced use and comprehensive documentation of the format, please see: +# https://docs.haskellstack.org/en/stable/configure/yaml/ + +# A 'specific' Stackage snapshot or a compiler version. +# A snapshot resolver dictates the compiler version and the set of packages +# to be used for project dependencies. For example: +# +# snapshot: lts-23.0 +# snapshot: nightly-2024-12-13 +# snapshot: ghc-9.8.4 +# +# The location of a snapshot can be provided as a file or url. Stack assumes +# a snapshot provided as a file might change, whereas a url resource does not. +# +# snapshot: ./custom-snapshot.yaml +# snapshot: https://example.com/snapshots/2024-01-01.yaml +# snapshot: +# url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/23/21.yaml + +snapshot: lts-23.27 + +# User packages to be built. +# Various formats can be used as shown in the example below. +# +# packages: +# - some-directory +# - https://example.com/foo/bar/baz-0.0.2.tar.gz +# subdirs: +# - auto-update +# - wai +packages: +- . +- ./Euterpea2 +- ./PortMidi +- ./vty-windows + +# Dependency packages to be pulled from upstream that are not in the snapshot. +# These entries can reference officially published versions as well as +# forks / in-progress versions pinned to a git hash. For example: +# +# extra-deps: +# - acme-missiles-0.3 +# - git: https://github.com/commercialhaskell/stack.git +# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a +# +# extra-deps: [] + +extra-deps: +- Win32-2.14.2.1 +- directory-1.3.9.0 +- haskeline-0.8.3.0 +- process-1.6.26.1 +- time-1.12.2 + +# Override default flag values for project packages and extra-deps +# flags: [] + +# Extra package databases containing global packages +# extra-package-dbs: [] + +# Control whether we use the GHC we find on the path +# system-ghc: true +# +# Require a specific version of Stack, using version ranges +# require-stack-version: -any # Default +# require-stack-version: ">=3.3" +# +# Override the architecture used by Stack, especially useful on Windows +# arch: i386 +# arch: x86_64 +# +# Extra directories used by Stack for building +# extra-include-dirs: [/path/to/dir] +# extra-lib-dirs: [/path/to/dir] +# +# Allow a newer minor version of GHC than the snapshot specifies +# compiler-check: newer-minor diff --git a/stack.yaml b/stack.yaml index 96a258c..9af86a1 100644 --- a/stack.yaml +++ b/stack.yaml @@ -46,15 +46,10 @@ packages: # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a # # extra-deps: [] -extra-deps: -- monomer-1.6.0.1 -- nanovg-0.8.1.0 -- sdl2-2.5.5.1 +# extra-deps: # Override default flag values for project packages and extra-deps -flags: - sdl2: - recent-ish: false +# flags: [] # Extra package databases containing global packages # extra-package-dbs: [] diff --git a/vty-windows b/vty-windows new file mode 160000 index 0000000..6f37185 --- /dev/null +++ b/vty-windows @@ -0,0 +1 @@ +Subproject commit 6f3718597657b4d7b31c186683082202e9404519